I have 2 tables, Departments and Employees. I want to display the department_id, department_name, and the number of employees in any department that has fewer than 4 employees.
Here's the code that I'm using (I use SQL developer, btw):
select d.department_id, d.department_name, count(e.last_name)
from departments d, employees e
where e.last_name < 4
group by d.department_id, d.department_name;
However, I'm getting a invalid number error. What is the correct way to do this?
Something like this would make more sense:
SELECT d.department_id,
d.department_name,
COUNT(*) AS numEmployees
FROM departments d
INNER JOIN employees e
ON d.department_id = e.department_id
GROUP BY d.department_id,
d.department_name
HAVING COUNT(*) < 4
Related
There is such a task: By joining the tables HR.DEPARTMENTS and HR.EMPLOYEES, display complete data on departments in which the minimum salary is below 5000.
I tried to do this, but it gives an error
select distinct d.department_id,department_name,
d.manager_id, location_id
from hr.departments d
left join hr.employees e on e.department_id = d.department_id
where min(e.salary) < 5000
order by 1
Error: group function is not allowed here
This is what hr.employees looks like:
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL PHONE_NUMBER HIRE_DATE JOB_ID SALARY COMMISSION_PCT MANAGER_ID DEPARTMENT_ID
100 Steven King SKING 515.123.4567 17-JUN-03 AD_PRES 24000 - - 90
hr.departments:
DEPARTMENT_ID DEPARTMENT_NAME MANAGER_ID LOCATION_ID
10 Administration 200 1700
You cannot use MIN in the WHERE clause, because MIN is an aggregation result over many rows, but in a WHERE clause you look at single rows (before any aggregation takes place).
The task to get the departments in question by joining the tables is a bit weird, because this is not how this should be done in SQL. If you must do it this way, then you only need a slight change to your query: Change the join into an inner join and check the rows' salary.
select distinct
d.department_id, department_name, d.manager_id, location_id
from hr.departments d
join hr.employees e on e.department_id = d.department_id
where e.salary < 5000
order by d.department_id;
The proper solution would use EXISTS or IN instead, so as not to create an unnecessarily large intermediate result that you must get rid of with DISTINCT:
select *
from hr.departments
where department_id in (select department_id from employees where salary < 5000)
order by department_id;
or
select *
from hr.departments d
where exists
(
select null
from employees e
where e.salary < 5000
and e.department_id = d.department_id
)
order by department_id;
This works for your solution, where is use for row filtering like gender = 'Male' while having is for aggregating filtering functions like min(salary) < 5000 but for having you need to group by with something like department.
SELECT
*
FROM
DimEmployee
WHERE
EmployeeID IN (
SELECT
EmployeeID
FROM
DimEmployee
GROUP BY
EmployeeID
HAVING
MIN(Salary) < 5000
)
First of all, don't use distinct, unless you have to. Secondly, you can't use group functions like that.
In order to solve this, you need to break the task into steps, breaking down your sentences.
"...the tables"
So we have this:
SELECT * FROM hr.departments;
... and ...
SELECT * FROM hr.employees;
"HR.DEPARTMENTS and HR.EMPLOYEES"
As you pointed our, the FK is the department.
(we first test the join, then add what we need)
(the 1 is just a placeholder; you can use EMPLOYEE_ID or COUNT(1), it's irrelevant)
SELECT 1
FROM hr.employees e
LEFT JOIN hr.departments d on e.department_id = d.department_id;
"display complete data on departments"
Well, this is simple, you just enumerate the columns you need or use d.*. We'll do this later.
"which the minimum salary is below 5000"
Now we get to the blocking issue. Let's list the records.
SELECT d.*
FROM hr.employees e, hr.departments d
WHERE e.department_id = d.department_id
AND EXISTS (SELECT 1 FROM hr.employees m WHERE m.department_id = d.department_id GROUP BY m.department_id HAVING min(m.salary) < 5000);
But what's this? We get a line for every employee of that department. Well, we can either use DISTINCT, but that is bad practice or we can fix the query.
We'll just remove the employees from the join.
SELECT d.*
FROM hr.departments d
WHERE EXISTS (SELECT 1 FROM hr.employees e WHERE e.department_id = d.department_id GROUP BY e.department_id HAVING min(e.salary) < 5000);
UPDATE:
To respect the task "By joining the tables"
So we have this:
SELECT d.*
FROM hr.departments d,
(
SELECT e.department_id
FROM hr.employees e
GROUP BY e.department_id
HAVING min(salary) < 5000
) e
WHERE e.department_id = d.department_id;
I need to select every department name and number of employees earning 10000 or more. That's my code.
SELECT d.department_name, COUNT(e.employee_id)
FROM hr.employees e
JOIN hr.departments d ON d.department_id=e.department_id WHERE e.salary > 10000
GROUP BY d.department_name;
It shows only departments which contains employees earning 10000 but I also need rest of departments with number 0 or - in column COUNT(e.employee_id). Can you help me?
You can do conditional aggregation. The idea is to not put the condition on employee salary in the join, but instead it in a conditional sum:
select
d.department_name,
sum(case when e.salary > 10000 then 1 else 0 end) cnt_high_salaries
from hr.department d
inner join hr.employee e on d.department_id = e.department_id
group by d.department_id, d.department_name
I think you just need outer join in your original query.
SELECT d.department_name,
COUNT(e.employee_id)
FROM hr.departments d
LEFT JOIN hr.employees e
ON d.department_id=e.department_id
and e.salary > 10000
GROUP BY d.department_name;
Is this what you are looking for?
SELECT d.department_name, COUNT(e.employee_id) FROM hr.employees e
JOIN hr.departments d ON d.department_id=e.department_id WHERE d.department_id in
(SELECT distinct e.department_id from employees e WHERE e.salary > 10000)
GROUP BY d.department_name;
I have some problems trying to print department_id, department_name and the sum of salaries of each department and I can't figure out why. I get the error: 00979 - "not a GROUP BY expression"
SELECT d.department_id, d.department_name, SUM(e.salary)
FROM departments d, employees e
WHERE d.department_id = e.department_id
GROUP BY d.department_id;
Just add the name to the group by expression. Along the way, also fix the query to use explicit join syntax:
SELECT d.department_id, d.department_name, SUM(e.salary)
FROM departments d JOIN
employees e
ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name;
Although you didn't ask, I will point out that your version of the query does make sense and is ANSI-compliant (although most databases don't support this feature). You are aggregating by a primary key, so bringing in additional columns is allowed -- although Oracle does not support this feature.
SELECT d.department_id, d.department_name, SUM(e.salary)
FROM departments d, employees e
WHERE d.department_id = e.department_id
GROUP BY d.department_id, d.department_name;
Just needed to add the department_name column in your group by clause.
Alternately, you can aggregate the department name as well using MIN() or MAX():
SELECT d.department_id, MAX(d.department_name) AS department_name
, SUM(e.salary) AS department_salary
FROM departments d INNER JOIN employees e
ON d.department_id = e.department_id
GROUP BY d.department_id;
Note that I updated your syntax from the old ANSI standard to the newer one. If you prefer the older syntax (as I do), then just use this:
SELECT d.department_id, MAX(d.department_name) AS department_name
, SUM(e.salary) AS department_salary
FROM departments d, employees e
WHERE d.department_id = e.department_id
GROUP BY d.department_id;
Hello guys and thank you in advance for your time and help.
So I am trying to get a list of the Department names their manager name and the total number of employees per department.
My code so far looks like this:
select d.department_name,e.first_name,e.last_name
from employees e, departments d
where e.department_id = d.department_id and d.manager_id=e.employee_id
group by d.department_name,e.first_name,e.last_name
order by d.department_name;
which produces the list of the manager per department,but I am still short of the count of employees per department. Any ideas?
You need to use the COUNT function. Try this:
select d.department_name,e.first_name,e.last_name,count(e.employee_id) as `TotalNoOfEmployees`
from employees e JOIN departments d
ON e.department_id = d.department_id and d.manager_id=e.employee_id
group by d.department_name,e.first_name,e.last_name
order by d.department_name;
Also try not to use the old way of Joining the tables ie, comma separated JOINS.
After a lot of experimentation I got it. Posting it in case somebody might find it useful someday:
select distinct d.department_name,
(select e.first_name||', '||e.last_name from employees e
where d.department_id=e.department_id and
d.manager_id=e.employee_id)as "manager_name",
( select count( employee_id ) from employees e
where d.department_id=e.department_id ) as "total_no_of_employees"
from employees e
join departments d on d.department_id=e.department_id
order by d.department_name;
Try this:
select emp.manager_id, mgr.first_name, mgr.last_name, dept.department_name, count(emp.employee_id)
from hr.employees emp
join hr.employees mgr
on emp.manager_id = mgr.employee_id
join hr.departments dept
on mgr.department_id = dept.department_id
group by emp.manager_id, mgr.first_name, mgr.last_name, dept.department_name
order by department_name
I have an issue in executing the below database query.
I am using Oracle 11g Enterprise Edition
Query 1:
SELECT d.department_id, max(salary), min(salary), avg(salary), count(*) no_of_employees
FROM departments d, employees e
WHERE e.department_id = d.department_id
GROUP BY d.department_id
Result: successful output
Query 2:
SELECT d.department_id, d.department_name, max(salary), min(salary), avg(salary), count(*) no_of_employees
FROM departments d, employees e
WHERE e.department_id = d.department_id
GROUP BY d.department_id
Result:
ORA-00979: not a GROUP BY expression
Can anybody help me out with this issue?
Please let me know what is wrong with this expression.
you must also group by department_name
You need to GROUP BY all the columns that you don't have an aggregation function (MAX, COUNT, etc). Therefore:
select d.department_id
, d.department_name
, max(salary), min(salary), avg(salary) , count(*) no_of_employees
from departments d, employees e
where e.department_id = d.department_id
group by d.department_id, d.department_name;
But you should also consider doing an ANSI join instead:
select department_id
, department_name
, max(salary), min(salary), avg(salary) , count(*) no_of_employees
from departments
join employees USING (department_id)
group by department_id, department_name;