How to fix group function nested too deeply error - sql

I've run into an error I've never seen before while trying to learn SQL where it is saying that my group by function is nested too deeply? Any ideas on how to fix this code would be greatly appreciated. I am trying to find the division with the most employees.
Tables:
division(DID, dname, managerID)
employee(empid, name, salary, DID)
Code:
select dname
from division
where did in (select did from employee group by did having max(count(empid)))

I don't have your tables, but Scott's sample schema contains similar tables - dept (departments) and emp (employees who work in these departments).
How many employees work in each department?
SQL> select deptno, count(*)
2 from emp
3 group by deptno
4 order by 2 desc;
DEPTNO COUNT(*)
---------- ----------
30 6 --> this is what you want, it has the most employees
20 5
10 3
Which department is it?
SQL> select * from dept order by deptno;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO --> it's SALES
40 OPERATIONS BOSTON
SQL>
This is what you tried: not that it won't work (Oracle doesn't let you), syntax is wrong. having clause should be something like having count(*) = 25, not just your wishes comes true (having max(count(*)) which reads as "give me department that has maximum count of employees").
SQL> select dname
2 from dept
3 where deptno in (select deptno from emp
4 group by deptno
5 having max(count(empno))
6 );
having max(count(empno))
*
ERROR at line 5:
ORA-00935: group function is nested too deeply
SQL>
So, what can we do about it? A simple option is to rank departments by number of employees:
SQL> select deptno,
2 count(*) cnt,
3 rank() over (order by count(*) desc) rnk
4 from emp
5 group by deptno;
DEPTNO CNT RNK
---------- ---------- ----------
30 6 1 --> department 30 ranks as the highest
20 5 2
10 3 3
SQL>
The rest is easy: use that query as a subquery (or a CTE, as I did), and select the one that ranks the highest:
SQL> with temp as
2 (select deptno,
3 count(*) cnt,
4 rank() over (order by count(*) desc) rnk
5 from emp
6 group by deptno
7 )
8 select d.dname
9 from dept d join temp t on t.deptno = d.deptno
10 where t.rnk = 1; --> the highest rank
DNAME
--------------
SALES
SQL>
SALES it is.

Related

Get oldest date in database SQL [duplicate]

This question already has answers here:
How do I limit the number of rows returned by an Oracle query after ordering?
(14 answers)
Closed 12 months ago.
select
max((date '2019-07-01' - applicant.applicantbirthdate) / 365 as age,
applicant.applicantfirstname,
applicant.applicantlastname,
office.officename
from
applicant
inner join
office on office.officeid = applicant.officeid
group by
applicant.applicantfirstname, applicant.applicantlastname,
office.officename;
I'm not really sure what to do here. I'm suppose to pick the oldest user and list their office. but this shows the oldest user for all offices. how can I just only show the oldest person out of all the offices
This example is based on Scott's sample schema whose tables simulate what you have.
SQL> SELECT ename, hiredate
2 FROM emp
3 ORDER BY hiredate DESC;
ENAME HIREDATE
---------- ----------
ADAMS 12.01.1983 --> Adams has the MAX hiredate; you want his data
SCOTT 09.12.1982
MILLER 23.01.1982
FORD 03.12.1981
JAMES 03.12.1981
KING 17.11.1981
MARTIN 28.09.1981
TURNER 08.09.1981
CLARK 09.06.1981
BLAKE 01.05.1981
JONES 02.04.1981
WARD 22.02.1981
ALLEN 20.02.1981
SMITH 17.12.1980
14 rows selected.
Using a CTE which includes the RANK analytic function that "sorts" rows by hiredate in descending order (so that people - who have the MAX hiredate - rank as "highest")), in final query you just fetch those whose rank = 1:
SQL> WITH
2 temp
3 AS
4 (SELECT e.ename,
5 d.dname,
6 e.hiredate,
7 RANK () OVER (ORDER BY e.hiredate DESC) rn
8 FROM emp e JOIN dept d ON e.deptno = d.deptno)
9 SELECT ename, dname, ROUND ((SYSDATE - hiredate) / 365) age
10 FROM temp
11 WHERE rn = 1;
ENAME DNAME AGE
---------- -------------- ----------
ADAMS RESEARCH 39
SQL>
Applied to your query:
WITH
temp
AS
(SELECT a.applicantfirstname,
a.applicantlastname,
o.officename,
a.birthdate,
RANK () OVER (ORDER BY a.birthdate DESC) rn
FROM applicant a JOIN office o ON o.officeid = a.officeid)
SELECT applicantfirstname,
applicantlastname,
officename,
ROUND ((SYSDATE - birthdate) / 365) AS age
FROM temp
WHERE rn = 1;
#The below code should work. I recommend to go with RANK over if its large dataset but if its small dataset the below code works fine as well.
select applicant.applicantfirstname, applicant.applicantlastname, office.officename, max((date '2019-07-01' - applicant.applicantbirthdate) / 365 as age
FROM
applicant
INNER JOIN office ON office.officeid = applicant.officeid
WHERE
applicant.applicantbirthdate IN (
SELECT MIN(applicant.applicantbirthdate) FROM applicant);
Try the below one. There are two things missing in your query.
We should list them based on descending order. So putting ORDER BY age DESC would ensure it.
We need to get the oldest candidate in terms of age. So LIMIT 1 would ensure that we would get only one record.
select (date '2019-07-01' - applicant.applicantbirthdate)/365 as age, applicant.applicantfirstname, applicant.applicantlastname, office.officename
from applicant
inner join office on office.officeid = applicant.officeid
group by applicant.applicantfirstname, applicant.applicantlastname, office.officename
order by age desc
limit 1;

Unable to MAX(COUNT) and have multiple columns

I have 4 tables, EMPLOYEE, DRIVER, TRIP and TRIPLEG
EMPLOYEE table has Name which I want to extract and show with MAX count, has E# which is shared with DRIVER
DRIVER table has L#(licence number) which is common with TRIP
TRIP table has T#(trip number) which is common with TRIPLEG
I'm trying to find the max number of tripleg a driver has done(In this case driver with licence number 10002:
SELECT MAX(COUNT(TRIPLEG.LEG#))
FROM TRIP, TRIPLEG
ON TRIP.T# = TRIPLEG.T#
WHERE TRIP.L# = 10002
GROUP BY TRIP.T#
COUNT(TRIPLEG.LEG#) gives me https://i.imgur.com/AYAovov.png,
so I did the above MAX(COUNT(TRIPLEG.LEG#)) which gives me this: https://i.imgur.com/alCFlO3.png
I'm unable to proceed as I tried SELECTING more columns(TRIP.T#) like
SELECT TRIP.T#, MAX(COUNT(TRIPLEG.LEG#))
FROM TRIP, TRIPLEG
ON TRIP.T# = TRIPLEG.T#
WHERE TRIP.L# = 10002
GROUP BY TRIP.T#
Gives me an error: ORA-00937: not a single-group group function
Any advice? Need to be able to start small and selecting before I can join more tables to get the Employee name displayed beside the MAX tripleg count
Thanks in advance
Essentially I want something like: (only 1 row, which is the MAX triplegs done (5))
NAME MAX(COUNT(TRIPLEG.LEG#))
-----------------------------------
BOB 5
I don't have your table so I'll use Scott's EMP and DEPT (as you use Oracle, so I presume you're familiar with them).
This works:
SQL> select d.dname, count(e.empno) cnt
2 from emp e join dept d on e.deptno = d.deptno
3 where d.deptno in (10, 20)
4 group by d.dname;
DNAME CNT
-------------- ----------
ACCOUNTING 3
RESEARCH 5 --> MAX count is this
SQL>
Nested count works if there are no other columns in SELECT column list (you already know that), and it returns desired value:
SQL> select max(count(e.empno)) cnt
2 from emp e join dept d on e.deptno = d.deptno
3 where d.deptno in (10, 20)
4 group by d.dname;
CNT
----------
5
SQL>
But, this won't work (you know that too):
select d.dname, max(count(e.empno)) cnt
from emp e join dept d on e.deptno = d.deptno
where d.deptno in (10, 20)
group by d.dname;
To fix it, use CTE (Common Table Expression a.k.a. the WITH factoring clause) or an inline view; I'll show you the first option, with yet another addition: I'll rank counts and find the "highest" one, and use it later to select only desired row.
SQL> with tcnt as
2 (select d.deptno,
3 d.dname,
4 count(e.empno) cnt,
5 rank() over (order by count(e.empno) desc) rnk --> rank them DESC
6 from emp e join dept d on e.deptno = d.deptno
7 where d.deptno in (10, 20)
8 group by d.dname, d.deptno
9 )
10 select t.deptno, t.dname, t.cnt
11 from tcnt t
12 where rnk = 1; --> fetch only row(s) with highest rank
DEPTNO DNAME CNT
---------- -------------- ----------
20 RESEARCH 5
SQL>
Finally, add some more columns from other tables:
SQL> with tcnt as
2 (select d.deptno,
3 d.dname,
4 count(e.empno) cnt,
5 rank() over (order by count(e.empno) desc) rnk
6 from emp e join dept d on e.deptno = d.deptno
7 where d.deptno in (10, 20)
8 group by d.dname, d.deptno
9 )
10 select t.deptno, t.dname, t.cnt, e.ename, e.job
11 from tcnt t join emp e on e.deptno = t.deptno
12 where rnk = 1;
DEPTNO DNAME CNT ENAME JOB
---------- -------------- ---------- ---------- ---------
20 RESEARCH 5 SMITH CLERK
20 RESEARCH 5 JONES MANAGER
20 RESEARCH 5 SCOTT ANALYST
20 RESEARCH 5 ADAMS CLERK
20 RESEARCH 5 FORD ANALYST
SQL>
Based on your tables to find the max number of tripleg a driver has done(In this case driver with licence number 10002:
Select a.tripno, max(a.trips)
From
(Select Trip.T# as tripno, count(tripleg.leg#) as trips
From tripleg join trip on tripleg.T# = trip.T#
where trip.L# = 10002
Group by tripno
) a
Group by a.tripno

How to save data after using With Clause?

I want to save the data I have after With Clause. Maybe saving into TEMP table or some other data set.
WITH TASKLIST AS
(SELECT * FROM IC_V_NEWSKUSTASKLIST WHERE MSTID IS NULL),
RESULTS AS
(SELECT *
FROM ICTASK
LEFT JOIN TASKLIST
ON ICTASK.SOURCETABLEID = TASKLIST.SOURCETABLEID)
SELECT * FROM RESULTS;
I want to save RESULTS data into some other table in stand of SELECT them, but the select statement is mandatory after WITH clause.
CTAS (Create Table As Select) is one option:
SQL> create table test as
2 with
3 tasklist as
4 (select deptno, empno, ename, job, sal
5 from emp
6 ),
7 results as
8 (select d.dname, t.deptno, t.ename
9 from tasklist t join dept d on d.deptno = t.deptno
10 where t.deptno = 10
11 )
12 select * from results;
Table created.
SQL> select * from test;
DNAME DEPTNO ENAME
-------------- ---------- ----------
ACCOUNTING 10 CLARK
ACCOUNTING 10 KING
ACCOUNTING 10 MILLER
SQL>

SQL Query to Retrieve the details of those employees who works in a department having head count more than 5

I have the following Table.
STAFF
STAFFNO STAFFNAME DESIGNATI SALARY DEPTNO
---------- ---------- --------- ---------- ----------
1000 Rajesh Manager 35000 1
1001 Manoj Caretaker 7420.35 1
1002 Swati HR 22500 3
1003 Suresh HR 23400 3
1004 Najim Mangager 17200 2
1006 Ritesh Prgrmr 23500 2
1005 Nisha Prgrmr 24852 1
1007 Rajib Security 6547 3
1008 Neeraj Prgrmr 17300 1
1009 Dushant Prgrmr 16500 1
1010 Pradyut Manager 26300 2
1011 Manisha Prgrmr 21500 2
1012 Janak Security 8500 2
Now I want to run a query on oracle (SQL*Plus) in which I can retrieve the details of those employees who works in a department having 5 or more head count.(e.g. deptno. 1 and deptno. 2 have 5 employees working in them)
Can you help me with the Oracle query to retrieve that? Thanks in advance.
You need create a sub query or perform a JOIN.
With a JOIN first you need to know what department has more that 5 employees.
SELECT DEPTNO
FROM STAFF
GROUP BY DEPTNO
HAVING COUNT(*) >= 5
Now you join both result
SELECT S.*
FROM STAFF S
JOIN ( SELECT DEPTNO
FROM STAFF
GROUP BY DEPTNO
HAVING COUNT(*) >= 5 ) F
ON S.DEPTNO = F.DEPTNO
Subquery version:
SELECT S.*
FROM STAFF S
WHERE S.DEPTNO IN ( SELECT DEPTNO
FROM STAFF
GROUP BY DEPTNO
HAVING COUNT(*) >= 5 )
If you want the employee detail, then you actually want an analytic function:
select s.*
from (select s.*, count(*) over (partition by deptno) as deptcnt
from staff
) s
where deptcnt >= 5;
It should be like this
SELECT * FROM STAFF WHERE DEPTNO IN
(SELECT DEPTNO FROM STAFF GROUP BY DEPTNO HAVING COUNT(*)>4)
Here it is using a join (because no one has) it may allow you to change your BI rules easier....
SELECT S.*
FROM STAFF S
LEFT JOIN (
SELECT DEPTNO, COUNT(*) AS C
FROM STAFF
GROUP BY DEPTNO
) AS D_COUNTS ON S.DEPTNO = D_COUNT.DEPTNO
WHERE D_COUNTS.C >= 5
or as a CTE
WITH D_COUNTS AS
(
SELECT DEPTNO, COUNT(*) AS C
FROM STAFF
GROUP BY DEPTNO
)
SELECT S.*
FROM STAFF S
LEFT JOIN D_COUNTS ON S.DEPTNO = D_COUNT.DEPTNO
WHERE D_COUNTS.C >= 5

SQL select records common to a column

I have an emp table like this
EMPNO | JOB | DEPTNO
_____________________
1 | A | 10
2 | B | 20
3 | C | 10
4 | A | 20
I want to write a query to List the jobs common to department 10 and 20.
So the output as per the data provided should be
A
which is common to both deptno 10 and 20
I am trying this but it doesn't give me any output.
select job, deptNo from emp group by job having deptno = 20 and deptno = 10; /*6*/
Try this
select job
from emp e1
where e1.deptno = 10
and exists ( select 1
from emp e2
where e2.deptno = 20
and e1.job = e2.job
)
group by job;
You can use an aggregate function in the HAVING clause with the condition you need:
SELECT job
FROM emp
GROUP BY job
HAVING COUNT(CASE WHEN deptNo IN (10, 20) THEN 1 END) = 2
The fields can't be both values at the same time. Use an Or instead of And. Also, the Having doesn't quite make sense, just use Where.
select job, deptNo
from emp
Where deptno = 20
Or deptno = 10
group by job
You can also use an In, which would have the same effect as an Or:
select job, deptNo
from emp
Where deptno In (10, 20)
group by job
Edit: You can do this to get the Jobs that have both values:
Select Distinct a.job
From emp a
Where DeptNo = 10
And Exists
(
Select 1
From Emp b
Where a.Job = b.Job
And b.deptNo = 20
)