Selected non-aggregate values must be part of the associated group - sql

I have two tables in Teradata: Table_A and Table_B. Between them is LEFT JOIN. Afterwards I am making SELECT statement which contains attributes from both tables:
SELECT
attribute_1
attribute_2
...
attribute_N
Afterwords, I am using SUM functions to do certain calculations. These functions look something like this:
SUM (
CASE WHEN Attribute_1 > 2 THEN attribute_2*1.2
ELSE 0
End
(in this example attributes in the select part are used).
But I also use in CASE part attributes which are not in the select statement - something liek this:
SUM (
CASE WHEN Attribute_X > 2 THEN attribute_Y*1.2
ELSE 0
End
Of course at the end I am doing GROUP BY 1,2,...,N
The error I am getting is "Selected non-aggregate values must be part of the associated group."
Furtheremore, I have checked billion times the number of the selected attributes in the SELECT part, and it is N.
The question is - why am I getting this error? Is it because I am using in the SUM part i.e. CASE part attributes (attribute_X and attribute_Y) which are not included in the SELECT part?
Blueprint of the end-statement looks sthg. like this:
INSERT INTO table_new
SELECT
attribute_1,
attribute_2,
...
attribute_N,
SUM (
CASE WHEN Attribute_1 > 2 THEN attribute_2*1.2
ELSE 0
End
) as sum_a,
SUM (
CASE WHEN Attribute_X > 2 THEN attribute_Y*1.2
ELSE 0
End
) as sum_X
FROM table_a LEFT JOIN table_B
ON ...
GROUP BY 1,2,...,N

The error message suggests that you have not included all the non-aggregate columns listed in your SELECT statement in your GROUP BY expression. I'm guessing that you have more columns listed than you have "place holders".
The best way to avoid this is to explicitly name all the columns and not use the "relative positioning" syntax. In other words, rather than using GROUP BY 1,2,...N use:
GROUP BY
attribute_1,
attribute_2,
...
attribute_N
If that does not fix your problem, modify your question and show a complete query that is not working.

I have found the error - the SUM part was composed of more sub-parts. For example "amount - SUM(...) + SUM(...)". I had ti include the attributes in the "amount" part.

Related

Display 0 where condition doesnt match

I need to write a query where I need to display 0 where source system havent sent any data for today, if I write simple query with count(*) it provides me no rows. I am using case statement but not successful to display actual count.
select SRC_SYS,
case
when ( select count (*) from table1 where SRC_SYS ='A')='0' then '0'
else count(1) end as Count
This is working fine when there is no data, but when I have data its not displaying the actual count
when I have data its not displaying actual count
Er.. No, indeed it won't; it will display "1" because there is only one row; the dataset you're count(1)ing has only one row
Just do the count from the actual table; it'll work fine whether there are 0 rows or more because the select list only contains constants or aggregates:
select 'A' as SRC_SYS, COUNT(*)
from table1
where SRC_SYS ='A'
Or if your front end is using parameters:
select #pwhatever as SRC_SYS, COUNT(*)
from table1
where SRC_SYS = #pwhatever
See this fiddle and change the where 1=0 to where 1=1 to see the count change

SQL: Group by Case Statement to sum Row Values

I have a table that looks like below. I am trying to group by subject using a case statement for every "subject" that starts with "cf" like something like the following:
SELECT CASE WHEN subject LIKE '%cf' THEN sum(Count)
FROM table
The goal is to just drop all the extra characters following "cf" and then sum the total count and create a new row as 'Cf' with the sum of count value as shown in the desired output.
subject. count
status 2461193
priority 1042073
ta. 126295
dueDate 62560
assignee 34142
cf2122 1
cf2123. 1
cf4312. 1
cf3234. 1
cf5464. 1
desired output:
subject. count
status 2461193
priority 1042073
ta. 126295
dueDate 62560
assignee 34142
cf. 1221
You want a GROUP BY based on an expression. You also need to put the wildcard for the LIKE condition after the prefix you want, 'cf%'
SELECT CASE
WHEN subject LIKE 'cf%' THEN 'cf'
else subject
end as normalized_subject,
sum("count")
FROM the_table
group by normalized_subject
order by sum("count") desc
Using a column alias in the GROUP BY is a Postgres extension to the SQL language. In other DBMS products you would need to repeat the CASE expression in the GROUP BY (which is only more to type, but won't change performance)
Online example

Specifying a column value in an aggregate function vs using a WHERE clause

I have a database people that looks like this:
I wanted to count the occurrences of state='CA'.
My first attempt was:
SELECT COUNT(state='CA')
FROM people
;
this returned 1 row with a value of 1000. So I thought that there were 1000 people from CA in the database.
This turns out to be incorrect. I know that they are 127, which I can verify with the query
SELECT COUNT(*)
FROM people
WHERE state='CA'
;
which returns 1 row with a value of 127.
I understand how the second query works. However, I do not understand what is wrong with the first one. What is it returning?
If you want to see what's going on, run the query:
select state='CA' from people;
You will see that you will get one result for each row in people, with the value 0 or 1 (or True/False). What you've selected is whether state='CA' for each row, and there will be just as many of those results as there are rows.
You can't constrain a COUNT statement within the statement, you have to do that via the WHERE clause as in your second example.
count is not a sum .. your first query is improper because don't return the number of the rows true .. but the total numbers of not null rows true or false
if you want a filter count you must use a where condition (as your second query) otherwise you must use an if or a a select case inside the sum() function eg:
Select sum(case
when state='CA' then 1 else 0
end) as my_result from People;
or if you want count .. use null and not 0min count
Select count(case
when state='CA' then 1 else null
end) as my_result from People;
Try this-:
Select count(case when state='CA' then 1 else null end) as xyz from People;
1st query will work if you use case when in side count,
like below query will returned count of CA
SELECT sum( case when state='CA' then 1 else 0 end)
FROM people
In first query it is assigning the value 'CA' to the column state for all 1000 rows instead of filtering the values. That is what SELECT does. SELECT does not filter the number of returning rows, it modifies the data.
Whereas in WHERE clause the rows are being filtered first then the SELECT clause runs the COUNT function.
There is a sequence for running the query. It starts from FROM then WHERE, GROUP BY, ORDER BY at the end SELECT will run.
To answer the actual question - why do you get 1000? I'm guessing that there are 1000 rows in your database, or at least 1000 where state is not null. Count will return the number of rows where the thing inside the () is not null and as one of your comments says, the part inside your () will return either true or false, neither of which is null, so will count them all. Your second example is of course the right way to do it.

Emulate subquery with no main table in access

I can do this in SQL Server:
SELECT 'HERRAMIENTA ELÉCTRICA' AS TIPO_PRODUCTO,
0 AS DEPRECIACION,
(select sum(empid) from HR.employees) STOCK
but in Access the same query show me the next error:
Query input must contain at least one table or query
So which could be the best form to emulate this? Make a query with any other table looks dirty for me.
EDIT 1:, HR.employees It may no have data, but i want show constants ('HERRAMIENTA ELÉCTRICA',''0') and 0 in the third column, maybe using isnull and this is not the problem here.
Why not to select directly:
select 'HERRAMIENTA ELÉCTRICA' AS TIPO_PRODUCTO,
0 AS DEPRECIACION,
IIF(ISNULL(sum(empid)), 0, sum(empid)) AS STOCK
from HR.employees
This simply doesn't work in Access. You need a FROM clause.
So you need to have a dummy table with one record, even if you don't use a single field from that table.
SELECT 'HERRAMIENTA ELÉCTRICA' AS TIPO_PRODUCTO,
0 AS DEPRECIACION,
(select sum(empid) from HR.employees) STOCK
FROM Dummy_Table
Using this example as empty table:
with employ as
(select 2 as col from dual
minus
select 2 as col from dual)
The query is this one:
select 'HERRAM' as tipo,
0 as deprec,
coalesce(sum(col), 0) as STOCK
from employ;
coalesce(x, value) sets the column to value when X is null
In Access, you can use a system table, and Val and Nz for the zero value:
SELECT TOP 1
'HERRAMIENTA ELÉCTRICA' AS TIPO_PRODUCTO,
0 AS DEPRECIACION,
Val(Nz((select sum(empid) from HR.employees), 0)) AS STOCK
FROM
MSysObjects

How to do this query in T-SQL

I have table with 3 columns A B C.
I want to select * from this table, but ordered by a specific ordering of column A.
In other words, lets' say column A contains "stack", "over", "flow".
I want to select * from this table, and order by column A in this specific ordering: "stack", "flow", "over" - which is neither ascending nor descending.
Is it possible?
You can use a CASE statement in the ORDER BY clause. For example ...
SELECT *
FROM Table
ORDER BY
CASE A
WHEN 'stack' THEN 1
WHEN 'over' THEN 2
WHEN 'flow' THEN 3
ELSE NULL
END
Check out Defining a Custom Sort Order for more details.
A couple of solutions:
Create another table with your sort order, join on Column A to the new table (which would be something like TERM as STRING, SORTORDER as INT). Because things always change, this avoids hard coding anything and is the solution I would recommend for real world use.
If you don't want the flexibility of adding new terms and orders, just use a CASE statement to transform each term into an number:
CASE A WHEN 'stack' THEN 1 WHEN 'over' THEN 2 WHEN 'flow' THEN 3 END
and use it in your ORDER BY.
If you have alot of elements with custom ordering, you could add those elements to a table and give them a value. Join with the table and each column can have a custom order value.
select
main.a,
main.b,
main.c
from dbo.tblMain main
left join tblOrder rank on rank.a = main.a
order by rank.OrderValue
If you have only 3 elements as suggested in your question, you could use a case in the order by...
select
*
from dbo.tblMain
order by case
when a='stack' then 1
when a='flow' then 2
when a='over' then 3
else 4
end