Regarding the working mechanism of NOT IN clause in Oracle SQL - sql

Following are the results of my sql queries:
SELECT DISTINCT(department_id)
from employees
ORDER BY department_id;
Result:
DEPARTMENT_ID
10
20
30
40
50
60
70
80
90
100
110
Then:
SELECT department_id
FROM departments
ORDER BY department_id;
DEPARTMENT_ID
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240
250
260
270
When I execute the Following Query, I get the result:
SELECT department_id
from departments
where department_id IN (select distinct(department_id) from employees)
ORDER BY department_id;
DEPARTMENT_ID
10
20
30
40
50
60
70
80
90
100
110
However, the following query returns "NO Rows Selected" when I execute the following query:
SELECT department_id
from departments
WHERE department_id NOT IN (select distinct(department_id) from employees)
ORDER BY department_id;
What I was expected was the Id of departments that are not present in Employees table.
I feel its a beginner's mistake however i am not able to resolve this issue. Any help would be largely appreciated.

This is because at least one department_id in employees isNULL. When any value in theNOT INlist isNULL`, no rows are returned at all.
To fix this, I simply recommend always using NOT EXISTS with a subquery:
SELECT d.department_id
FROM departments d
WHERE NOT EXISTS (SELECT 1 FROM employees e WHERE d.department_id = e.department_id)
ORDER BY d.department_id;
(Or by using a LEFT JOIN/WHERE.)
You could fix this using a WHERE clause in the subquery. I think it is better to use a construct that does what you intend.

Related

what does count(*) do exactly? [duplicate]

This question already has answers here:
What is the difference between count(0), count(1).. and count(*) in mySQL/SQL?
(9 answers)
Closed 3 years ago.
I use oracle 11g. I want maximum and minimum salary, and count of employees for each department, so I do this :
select d.department_id, d.department_name, max(salary), min(salary), count(*)
from employees e ,
departments d
where e.department_id = d.department_id
group by d.department_id, d.department_name;
and it works :
DEPARTMENT_ID DEPARTMENT_NAME MAX(SALARY) MIN(SALARY) COUNT(*)
------------- ------------------------------ ----------- ----------- ----------
100 Finance 12008 6900 6
50 Shipping 8200 2100 45
70 Public Relations 10000 10000 1
30 Purchasing 11000 2500 6
90 Executive 24000 17000 3
10 Administration 4400 4400 1
110 Accounting 12008 8300 2
40 Human Resources 6500 6500 1
20 Marketing 13000 6000 2
60 IT 9000 4200 5
80 Sales 14000 6100 34
11 rows selected.
So what does count(*) mean? Count departments or what ?
count(*) returns the rows that match the where statement.
In your case its where e.department_id=d.department_id.
Because you applied a group by your count(*) will return the count of rows for each group (d.department_id).
This means you will get the count of employees (e.department_id) for each department (d.department_id).

SQL with nested group function

I'm using Oracle db 11g
I have a table 'EMPLOYEES' like this...
ID JOB_ID SALARY
100 AD_PRES 24000
101 AD_VP 17000
102 AD_VP 17000
103 IT_PROG 9000
104 IT_PROG 6000
107 IT_PROG 4200
124 ST_MAN 5800
141 ST_CLERK 3500
142 ST_CLERK 3100
143 ST_CLERK 2600
144 ST_CLERK 2500
149 SA_MAN 10500
174 SA_REP 11000
176 SA_REP 8600
178 SA_REP 7000
200 AD_ASST 4400
201 MK_MAN 13000
202 MK_REP 6000
205 AC_MGR 12000
206 AC_ACCOUNT 8300
And I want to get the maximum of average salary of each job(job_id) and the job of it.
I firstly tried this and It resulted in error
SELECT MAX(AVG(salary)) AS max_avg_salary, job_id
FROM employees
GROUP BY job_id;
ORA-00937: not a single-group group function
00937. 00000 - "not a single-group group function"
*Cause:
*Action:
I finally made it with this code
WITH emp AS (SELECT AVG(salary) AS avg_salary, job_id FROM employees GROUP BY job_id)
SELECT e1.avg_salary AS max_avg_salary, e1.job_id
FROM emp e1 JOIN (SELECT MAX(avg_salary) AS max_avg_salary FROM emp) e2
ON e1.avg_salary = e2.max_avg_salary;
MAX_AVG_SALARY JOB_ID
24000 AD_PRES
What I want to know is..
Why my first code makes error?
Is there any better (more simple or easier) way than my code?
How about this?
select job_id, salary
from ( select job_id,
avg (salary) salary,
rank () over (order by avg (salary) desc) rnk
from employees
group by job_id)
where rnk = 1;
you can modify your query like this,
SELECT MAX(avg_salary)
FROM (SELECT AVG(salary) AS avg_salary, job_id
FROM employees
GROUP BY job_id);
or if you want to display the job id also,
SELECT avg_salary, job_id
FROM (SELECT AVG(salary) AS avg_salary, job_id
FROM employees
GROUP BY job_id
ORDER BY avg_salary DESC)
WHERE ROWNUM = 1;
[Edited] ... Unless you meant to get just a single answer, in which case the other respondent's solution should work and I'll offer a variation:
Non-Oracle:
SELECT TOP 1 *
FROM
(SELECT AVG(salary) AS avg_salary, job_id
FROM employees
GROUP BY job_id
) a
ORDER BY a.avg_salary DESC
Littlefoot pointed out, quite correctly, that this won't work on Oracle because of the TOP 1. You should pick his solution. I'm going to leave this here for any non-Oracle folks:
However, the asker himself, C Park, suggested an Oracle-compatible variation on this using ROWNUM. He's tested it and it worked for him so this will work in..
Oracle:
SELECT *
FROM
(SELECT AVG(salary) AS avg_salary, job_id
FROM employees
GROUP BY job_id
) a
ORDER BY a.avg_salary DESC
WHERE ROWNUM = 1
I hope this helps.

Oracle SQL, Combine rows based off of corresponding attributes from another select

I want to combine the "COUNT(DEPARTMENT_ID)" column based on if the "LOCATION_ID" are the same as the corresponding "DEPARTMENT_ID" from another select statement. I have this select statement that gives me how many times "DEPARTMENT_ID" comes up.
select DEPARTMENT_ID, COUNT(DEPARTMENT_ID) FROM EMPLOYEES GROUP BY DEPARTMENT_ID;
Output:
DEPARTMENT_ID COUNT(DEPARTMENT_ID)
------------- --------------------
100 6
30 6
0
90 3
20 2
70 1
110 2
50 45
80 34
40 1
60 5
DEPARTMENT_ID COUNT(DEPARTMENT_ID)
------------- --------------------
10 1
12 rows selected.
And I have this other select that tells me what "DEPARTMENT_ID" corresponding "LOCATION_ID" is but I am not sure how to combine the "COUNT(DEPARTMENT_ID)" based on if the "LOCATION_ID" are the same.
select DEPARTMENT_ID, LOCATION_ID from DEPARTMENTS;
Output:
DEPARTMENT_ID LOCATION_ID
------------- -----------
10 1700
20 1800
30 1700
40 2400
50 1500
60 1400
70 2700
80 2500
90 1700
100 1700
110 1700
DEPARTMENT_ID LOCATION_ID
------------- -----------
120 1700
130 1700
140 1700
150 1700
160 1700
170 1700
180 1700
190 1700
200 1700
210 1700
220 1700
DEPARTMENT_ID LOCATION_ID
------------- -----------
230 1700
240 1700
250 1700
260 1700
270 1700
27 rows selected.
Any Ideas?
If your objective is to simply count the number of departments based on its location then the below query would be suffice.
select LOCATION_ID, count(DEPARTMENT_ID) from DEPARTMENTS group by LOCATION_ID
If you need count of employees based on the location the you need to use inner join and group by location id.
select t2.LOCATION_ID, COUNT(t1.DEPARTMENT_ID)
FROM EMPLOYEES t1 INNER JOIN (select DEPARTMENT_ID, LOCATION_ID from DEPARTMENTS) t2
on t1.DEPARTMENT_ID = t2.DEPARTMENT_ID GROUP BY t2.LOCATION_ID ;
Updated code:
You can use Inner Join, the INNER JOIN keyword selects records that have matching values in both tables. You can use following query.
select t1.DEPARTMENT_ID, COUNT(t1.DEPARTMENT_ID) ,t2.LOCATION_ID FROM EMPLOYEES t1 INNER JOIN (select DEPARTMENT_ID, LOCATION_ID from DEPARTMENTS) t2
on t1.DEPARTMENT_ID = t2.DEPARTMENT_ID GROUP BY t1.DEPARTMENT_ID,t2.LOCATION_ID ;

SQL using select with group by

SELECT manager_id, COUNT(manager_id)
FROM employees
GROUP BY manager_id
HAVING COUNT(manager_id) > 3
I have no problem with the code, but,I want to display the manager first name and last name instead of the manager_id, however if I do this, I get the error:
"column 'employees.first_name' is invalid... not contained in either an aggregate function or group by clause". I tried adding first_name and last_name to the group by, output being blank. Tried self join as well.. can't figure out the answer.
The query includes: employee_id, first_name, last_name, manager_id
The query above displays:
manager_id (No column name)
100 14
120 8
121 8
122 8
123 8
124 8
145 6
146 6
147 6
148 6
149 6
I want:
first_name last_name
Bob Smith
SELECT ISNULL(m.name, 'None') AS ManagerName, COUNT(1)
FROM employees e
LEFT JOIN employees m
on e.manager_id = e.employee_id
GROUP BY ISNULL(m.name, 'None')
HAVING COUNT(1) > 3
Note count(1) is the same as count(*) but slightly less work on the server.

Oracle, row_number, deleting rows

I have a little problem. Here's a code:
select * from
(select department_id, manager_id,last_name, salary, row_number() over
(partition by department_id,manager_id order by salary asc) p_number
from employees )
where p_number <=3;
This query shows 3 the least salaries in every department for every manager in department
ex: Dep no.30 has 2 managers (100 and 114), manager no.100 has 1 employee beneath him, manager no.114 - 3 employees.
part of result:
20 100 Hartstein 13000 1
20 201 Fay 6000 1
30 100 Raphaely 11000 1
30 114 Colmenares 2500 1
30 114 Himuro 2600 2
30 114 Tobias 2800 3
Now, i want to delete all rows, where there is only 1 employee beneath manager. In this example there should be, Harstein, Fay, Raphaely. Colmenares also has number 1, but there is more employees under manager 114.
Any ideas?
PS Having Count is out, because there is no group by, and modifying
where p_number <=3;
into
where p_number <=3 and p_number >1;
is also out, because it will delete all my employees with no.1 and i want to 'safe' few as they have more 'colleagues' :)
Thanks!
This will give the result and only require a single table scan:
SELECT *
FROM (
SELECT department_id,
manager_id,
last_name,
salary,
ROW_NUMBER()
OVER ( PARTITION BY department_id, manager_id
ORDER BY salary ASC ) AS p_number,
COUNT(*)
OVER ( PARTITION BY department_id, manager_id ) AS p_count
FROM employees
ORDER BY department_id, manager_id, salary
)
WHERE p_count >= 2
AND p_number <= 3;
Output:
DEPARTMENT_ID MANAGER_ID LAST_NAME SALARY P_NUMBER P_COUNT
------------- ---------- ---------- ---------- ---------- ----------
30 114 Colmenares 2500 1 3
30 114 Himuro 2600 2 3
30 114 Tobias 2800 3 3