sql Group by does not work on selecting multiple columns - sql

I have following query
SELECT ACTIONFINANCIALTRANSACTIONLOG.ID_CSE,
ACTIONFINANCIALTRANSACTIONLOG.DTE_PROC
FROM CSESDEV02.FINANCIAL_TRAN_LOG ACTIONFINANCIALTRANSACTIONLOG
GROUP BY ACTIONFINANCIALTRANSACTIONLOG.ID_CSE
HAVING COUNT(TXT_DESC) > 9
this results in
ORA-00979: not a GROUP BY expression
00979. 00000 - "not a GROUP BY expression"
*Cause:
*Action: Error at Line: 57 Column: 47
however, if i select only 1 column , it works
SELECT ACTIONFINANCIALTRANSACTIONLOG.ID_CSE
FROM CSESDEV02.FINANCIAL_TRAN_LOG ACTIONFINANCIALTRANSACTIONLOG
GROUP BY ACTIONFINANCIALTRANSACTIONLOG.ID_CSE
HAVING COUNT(TXT_DESC) > 9
why is that?

Although there's no aggregate function in SELECT column list, all columns that aren't aggregated should be part of the group by clause.
If there are 2 columns you're selecting, both have to be part of the group by.
BTW, table alias should be short so that it "simplifies" query and makes it easier to read. You did the opposite.
This is what works OK - only one column in select and it is part of group by:
SQL> select deptno
2 from emp
3 group by deptno
4 having count(job) > 1;
DEPTNO
----------
30
20
10
This doesn't work: two columns in select list, only one in group by:
SQL> select deptno,
2 ename
3 from emp
4 group by deptno
5 having count(job) > 1;
ename
*
ERROR at line 2:
ORA-00979: not a GROUP BY expression
But, when they both are in group by, query is OK:
SQL> select deptno,
2 ename
3 from emp
4 group by deptno, ename
5 having count(job) > 1;
no rows selected
SQL>

Read documentation.
Gist:
SelectItems in the SelectExpression with a GROUP BY clause must
contain only aggregates or grouping columns.

This is happening because GROUP by require all the column defined in SELECT clause.
The SELECT statement used in the GROUP BY clause can only be used contain column names, aggregate functions, constants and expressions.
select count(1),deptno
from emp
group by deptno
having count(1) > 1;

ACTIONFINANCIALTRANSACTIONLOG.DTE_PROC is not the product of a summary function and is not mentioned in the GROUP_BY clause so you get the error you've gotten. Perhaps you could use:
SELECT ftl.ID_CSE,
ftl.DTE_PROC
FROM (SELECT af.ID_CSE,
COUNT(af.TXT_DESC) AS TXT_DESC_COUNT
FROM CSESDEV02.FINANCIAL_TRAN_LOG af
GROUP BY af.ID_CSE
WHERE COUNT(af.TXT_DESC) > 9) iq
INNER JOIN CSESDEV02.FINANCIAL_TRAN_LOG ftl
ON ftl.ID_CSE = iq.ID_CSE

Syntax aside, think about the aggregation group you are defining with your group by clause:
GROUP BY ID_CSE
means you want one row per ID_CSE. Examples would be average salary per department, total sales per region, vaccinations per country.
In that one row for each id_cse, what dte_proc should be displayed if there is more than one distinct value?
For example, say your data contains
ID_CSE DTE_PROC
------ ----------
1 X
1 Y
2 Z
Now if you query
select id_cse, date_proc, count(*) from financial_tran_log
group by id_cse
what results do you expect to see?
ID_CSE DTE_PROC COUNT(*)
------ -------- --------
1 ??? 2
2 Z 1
It can't be done, hence the syntax error.

Related

ORA-00904 : invalid identifier [duplicate]

This question already has answers here:
Using an Alias in a WHERE clause
(5 answers)
Closed 8 months ago.
I am only a beginner in SQL, but I've come across this annoying error. SQL is having an issue with the WHERE clause of this script:
SELECT
ITEM_ID, ITEM_PRICE, DISCOUNT_AMOUNT, QUANTITY,
(ITEM_PRICE*QUANTITY) AS price_total,
(DISCOUNT_AMOUNT*QUANTITY) AS discount_total,
((ITEM_PRICE-DISCOUNT_AMOUNT)*QUANTITY) AS item_total
FROM ORDER_ITEMS
WHERE item_total > 500
ORDER BY item_total;
I am receiving this error:
Error starting at line : 1 in command -
SELECT
ITEM_ID, ITEM_PRICE, DISCOUNT_AMOUNT, QUANTITY,
(ITEM_PRICE*QUANTITY) AS price_total,
(DISCOUNT_AMOUNT*QUANTITY) AS discount_total,
((ITEM_PRICE-DISCOUNT_AMOUNT)*QUANTITY) AS item_total
FROM ORDER_ITEMS
WHERE item_total > 500
ORDER BY item_total DESC;
Error at Command Line : 7 Column : 7
Error report -
SQL Error: ORA-00904: "ITEM_TOTAL": invalid identifier
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
I have no idea why it has no issue with price_total nor discount_total, but is reporting item_total as invalid. I am trying to first select only the items which have a total greater than 500 when the discount amount is subtracted and it is multiplied by the quantity. Then, I need to sort the results in descending order by item_total
An alias can be used in a query select list to give a column a different name. You can use the alias in GROUP BY, ORDER BY, or HAVING
clauses to refer to the column.
Standard SQL disallows references to column aliases in a WHERE clause. This restriction is imposed because when the WHERE clause is
evaluated, the column value may not yet have been determined.
So, the following query is illegal:
SQL> SELECT empno AS employee, deptno AS department, sal AS salary
2 FROM emp
3 WHERE employee = 7369;
WHERE employee = 7369
*
ERROR at line 3:
ORA-00904: "EMPLOYEE": invalid identifier
SQL>
The column alias is allowed in:
GROUP BY
ORDER BY
HAVING
You could refer to the column alias in WHERE clause in the following cases:
Sub-query
Common Table Expression(CTE)
For example,
SQL> SELECT * FROM
2 (
3 SELECT empno AS employee, deptno AS department, sal AS salary
4 FROM emp
5 )
6 WHERE employee = 7369;
EMPLOYEE DEPARTMENT SALARY
---------- ---------- ----------
7369 20 800
SQL> WITH DATA AS(
2 SELECT empno AS employee, deptno AS department, sal AS salary
3 FROM emp
4 )
5 SELECT * FROM DATA
6 WHERE employee = 7369;
EMPLOYEE DEPARTMENT SALARY
---------- ---------- ----------
7369 20 800
SQL>
Starting from Oracle 12c you could use CROSS APPLY to define expression and then you could refer to them in WHERE clause:
SELECT
o.ITEM_ID, o.ITEM_PRICE, o.DISCOUNT_AMOUNT, o.QUANTITY,
s.price_total, s.discount_total, s.item_total
FROM ORDER_ITEMS o
CROSS APPLY (SELECT ITEM_PRICE*QUANTITY AS price_total,
DISCOUNT_AMOUNT*QUANTITY AS discount_total,
(ITEM_PRICE-DISCOUNT_AMOUNT)*QUANTITY AS item_total FROM dual) s
WHERE s.item_total > 500
ORDER BY s.item_total;
You cannot use the column name which is used as alias one in the query
Reason:
The query will first checks for runtime at that time the column name "item_total" is not found in the table "ORDER_ITEMS" because it was give as alias which is not stored in anywhere and you are assigning that column in desired output only
Alternate:
If you want to use that type go with sub queries it's performance is not good but it is one of the alternate way
SELECT * FROM
(SELECT
ITEM_ID, ITEM_PRICE, DISCOUNT_AMOUNT, QUANTITY,
(ITEM_PRICE*QUANTITY) AS price_total,
(DISCOUNT_AMOUNT*QUANTITY) AS discount_total,
((ITEM_PRICE-DISCOUNT_AMOUNT)*QUANTITY) AS item_total
FROM ORDER_ITEMS) as tbl
WHERE tbl.item_total > 500
ORDER BY tbl.item_total;

I want to list data as shown in output table in SQL

HI guys i am using ORACLE 11G.
I have table like
dept employee
10 a
10 b
10 c
10 d
20 p
20 q
20 r
20 s
And i want output like this
dept employee
10 a
10 a,b
10 a,b,c
10 a,b,c,d
20 a,b,c,d,p
20 a,b,c,d,p,q
20 a,b,c,d,p,q,r
20 a,b,c,d,p,q,r,s
You can use a self-join with LISTAGG() function like the one below
SELECT MAX(e2.dept) AS dept,
LISTAGG(e2.employee,',') WITHIN GROUP (ORDER BY e1.employee) AS employees
FROM emp e1
JOIN emp e2
ON e1.employee >= e2.employee
GROUP BY e1.employee
ORDER BY dept, employees
Demo
It looks like you can simply use ListAgg as a windowing function:
Select dept, ListAgg(employee,',') Over (Order by dept, employee) as emp
From mytable
Order by dept, employee
There will be an extra comma in the ListAgg function but you can wrap it in a substr function to trim it off.
I would suggest a correlated subquery:
select e.*,
(select listagg(e2.employee, ',') within group (order by e2.employee)
from emp e2
where e2.employee <= e.employee
) as employees
from emp e;
Notes:
I often find that correlated subqueries are faster than join/group by, because they avoid the outer aggregation.
This assumes that the ordering is by employee, but that is not clear. It might also be by department. That would require adjusting the correlation clause.
Oracle has a default limit of 2000 or 4000 characters for listagg() results. If your table has any size or the names are long, you will exceed this threshold.

What is mean the condition COUNT(1) = 1 in oracle sql

I have two table and write select:
SELECT A.code, B.account, A.ano
FROM atable A, dtable B
where A.ano=B.dno
group by A.code, B.account, A.ano
having count(1)=1;
if I add condition having count(1)=1 I didn't get any result, why?
Query groups data per columns listed in the GROUP BY clause. HAVING restricts result set to those whose count (for those grouped columns) equals 1.
If query returns nothing, it means that there's not at least one combination of those columns that has only one row as a result.
For example (based on Scott's EMP table):
SQL> select job, count(*) from emp
2 group by job;
JOB COUNT(*)
--------- ----------
CLERK 4
SALESMAN 4
PRESIDENT 1 --> only one person is a president
MANAGER 3
ANALYST 2
Your query:
SQL> select job
2 from emp
3 group by job
4 having count(*) = 1;
JOB
---------
PRESIDENT
Let's set someone else to be a president:
SQL> update emp set job = 'PRESIDENT' where ename = 'ADAMS';
1 row updated.
The query doesn't return anything now (as there are 2 presidents):
SQL> select job
2 from emp
3 group by job
4 having count(*) = 1;
no rows selected
SQL>
As of count(1): no point in it, use count(*) instead.

DB2 SQL Having Clause with a Nested Table Expression

Return the department number and the total payroll for the department that has the highest payroll. Payroll is the sum of all salaries and commissions of the department. Use a having clause with a nested table expression.
select e0.deptno,
(select sum(sal+com) FROM emp
group by deptno
having sum(sal+com) >= all(select sum(sal+com)
from emp
group by deptno) )as top
from emp as e0
group by e0.deptno
;
But my result is not correct. Im not so sure if my nested table expression combined with the having clause is done the right way. Can someone, try to help me? Thanks in advance.
As far as concerned, you don't need a having clause for this. You can just aggregate by department, order the results by payroll and fetch the first record:
select deptno, sum(sal + com) payroll
from emp e
group by deptno
order by payroll desc
fetch first 1 rows only
If you do want to use having (or if you are using a version of db2 that does not support the fetch ... rows ... clause), then we can build on your initial idea as follows:
select deptno, sum(sal + com) payroll
from emp
group by deptno
having sum(sal + com) >= all (
select sum(sal + com) from emp group by deptno
)

can't we use select another column with max() in sql

I have a table emp (ename,eid,did,sal,sex) where column did is foreign key with another table dept (did,dname).
I want to get the max sal of the company along with name and did of the person who is getting it.
I am executing following query
select did,ename ,max(sal) from emp;
But the Result is :
ORA-00937: not a single-group group function
so My question is can't I have more than 1 columns ?
You can't use MAX aggregate function like this. Using MAX without a GROUP BY clause will just return one record with the maximum sal value. You are not allowed to place any more non-aggregated fields in the SELECT clause.
If you want to get the record having the maximum sal value, then you have to do a self-join:
select e1.*
from emp as e1
inner join (
select max(sal) as max_sal
from emp
) as e2 on e1.sal = e2.max_sal
Note: The above query will return more than one records from table emp, in case more than one records share the same maximum sal value.
Edit:
If you want to get the maximum salary per department then you have to include a GROUP BY clause in the derived table used:
select e1.*
from emp as e1
inner join (
select did, max(sal) as max_sal
from emp
group by did
) as e2 on e1.did = e2.did and e1.sal = e2.max_sal
You would have to group the results by the sal column:
select did,ename,max(sal) from emp group by sal
MAX operates over the group specified in a GROUP BY clause and finds the maximum value for each group. When not specifying a set of columns to group by, it finds the max for the entire results set.
You need to find the max sal, then you can query the rows that match:
select did, ename, sal
from emp
where sal = (select max(sal) from emp)