SQL group functions using joins - sql

Problem:
Create a list of department names, the manager id,
manager name (employee last name) of that department, and the average salary in each
department.
SELECT d.department_name, d.manager_id, AVG(e.salary)
FROM employees e
INNER JOIN departments d ON (e.department_id = d.department_id)
GROUP BY d.department_name, d.manager_id;
And it works nice, but when I add the e.last_name, I get all the last names from employees table.
I do believe the answer to be out here and not quite far, although out of my reach at this point.

In order to pull the name of the manager, you need to join employees again, this time on d.manager_id:
SELECT d.department_name, d.manager_id, m.name, AVG(e.salary)
FROM employees e
INNER JOIN departments d ON (e.department_id = d.department_id)
LEFT OUTER JOIN employees m ON (m.employee_id = d.manager_id)
GROUP BY d.department_name, d.manager_id, m.name;
The kind of join (inner or outer) is not essential here, because you group by d.manager_id.

It looks like you need to join d.manager_id to employees again to get the managers last_name:
SELECT d.department_name, d.manager_id, e2.last_name, AVG(e.salary)
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
INNER JOIN employees e2 ON d.manager_id = e2.employee_id
GROUP BY d.department_name, d.manager_id, e2.last_name

Related

Complex query - retrieve employees working in both two specific departments

I'm trying to figure a way to retrieve employees working in two different departments.
I have 3 simple tables:
employee (employee_id, employee_name)
department (department_id, department_name)
working (eid, did, work_time)
So I have tried to write a SQL query:
select employee_name
from employee, working,department
where eid = employee_id
and did = department_id
and department_name = 'software'
and dname = 'hardware';
But it doesn't work, what is my problem?
The problem is that you are requiring department to be both 'software' and 'hardware'. Also, dname is not a field.
Correcting your query:
select employee_name
from employee, working, department
where eid = employee_id and did = department_id
and (department_name = 'software' or department_name = 'hardware');
But I would prefer this kind of query:
SELECT DISTINCT e.employee_name
FROM employee e
JOIN working w ON w.eid = e.employee_id
JOIN department d ON d.department_id = w.did
WHERE d.department_name IN ('software', 'hardware');
That is to get employees that work in any of the two departments (or both).
If you want only employees that work in both departments, try this:
SELECT e.employee_id, e.employee_name
FROM employee e
JOIN working w ON w.eid = e.employee_id
JOIN department d ON d.department_id = w.did
WHERE d.department_name IN ('software', 'hardware')
GROUP BY e.employee_id HAVING COUNT(DISTINCT d.department_id) = 2;
Would something like this work for you?
SELECT
count(*) as cnt,
employee.employee_name
FROM
employee
JOIN working ON working.eid = employee.employee_id
JOIN department ON department.department_id = working.did
WHERE
department.department_name = 'software' or department.department_name = 'hardware'
GROUP BY employee.employee_name
HAVING cnt > 1
This would count each employee who is linked both software or hardware department. Or you can leave WHERE clause away to get all employees working more than one departments.
What is my problem?
There is no dname column in your tables.
You can simplify the problem as you don't need the department table since the working table contains the department id in the did column.
Then you need to GROUP BY each employee and find those HAVING a COUNT of two DISTINCT department ids:
SELECT MAX(e.employee_name)
FROM employee e
INNER JOIN working w
ON e.employee_id = w.eid
GROUP BY e.employee_id
HAVING COUNT(DISTINCT w.did) = 2
If you want to consider only the software and hardware departments then:
SELECT MAX(e.employee_name)
FROM employee e
INNER JOIN working w
ON e.employee_id = w.eid
INNER JOIN department d
ON w.did = d.department_id
WHERE d.department_name IN ('software', 'hardware')
GROUP BY e.employee_id
HAVING COUNT(DISTINCT w.did) = 2
You can easily obtain employees who work in one specific department:
select *
from Employee e inner join
Working w on e.employee_id = w.eid inner join
Department d on w.did = d.department_id
where d.name = 'software'
Now ambiguity cames. If you want to get all employees work either in software or in hardware:
-- Employees who work at either software or hardware or both departments
select *
from Employee e inner join
Working w on e.employee_id = w.eid inner join
Department d on w.did = d.department_id
where d.name = in ('software', 'hardware')
If you want to get employees who works in both software and hardware departments:
-- Employees who work in both hardware and software deparments simultaneously
select *
from Employee e inner join
Working w on e.employee_id = w.eid inner join
Department d on w.did = d.department_id
where d.name = 'software'
intersect
select *
from Employee e inner join
Working w on e.employee_id = w.eid inner join
Department d on w.did = d.department_id
where d.name = 'hardware'

i am not getting error missing keyword in my code

select e.department_id,e.salary,d.department_name
from employees e
join departments d
on d.department_id=e.department_id
inner join
(
select department_id,max(salary) as max_sal from employees
group by department_id
) as t
on e.department_id=t.department_id
where e.salary =t.max_sal;
Oracle does not support as for table aliases, so you can try:
select e.department_id, e.salary, d.department_name
from employees e join
departments d
on d.department_id = e.department_id inner join
(select department_id, max(salary) as max_sal
from employees
group by department_id
) t
on e.department_id = t.department_id
where e.salary = t.max_sal;
Of course, this would be better written using window functions, but this answers the question that you asked.

Right outer JOIN in SQL

I have to list all departments, which do not have any employees assigned to but I don't know how to solve this with a outer-join. The scheme of the database is:
My current statement is
SELECT DISTINCT d.department_name
FROM departments d
RIGHT OUTER JOIN employees e ON NOT e.department_id IS NULL;
but it doesn't work as it should.
My result is this:
It should look like this:
Does anybody know what's wrong?
You can right this as an outer left join like so, correlating on the department_id
SELECT d.department_name
FROM departments d
LEFT JOIN employees e ON e.department_id=d.department_Id
WHERE e.employee_id is null
Or a more intuitive version would be where there does not exist any employees for a department_id
SELECT d.department_name
FROM departments d
WHERE NOT EXISTS (SELECT * FROM employees e WHERE e.department_id=d.department_Id )

SQL-HR Schema, Retrieving the Dept.Names,managers and employees per dept

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

Difference between old and new SQL JOINS

I am currently learing SQL and I can't understand why these two queries return different numbers of rows (the first one returns 53, while the second returns 69).
SELECT d.department_name "dept_name",
j.job_title "job_title",
e.manager_id "manager_id",
MAX(e.salary) "max_salary",
SUM(e.salary) "sum_salary"
FROM employees e, jobs j, departments d
WHERE j.job_id = e.job_id AND
d.department_id(+) = e.department_id
GROUP BY GROUPING SETS( (d.department_name, j.job_title), (j.job_title,e.manager_id), ());
And the second one:
SELECT d.department_name AS "dept_name",
j.job_title AS "job_title",
e.manager_id AS "manager_id",
MAX(e.salary) AS "max_salary",
SUM(e.salary) AS "sum_salary"
FROM employees e
INNER JOIN jobs j ON j.job_id = e.job_id
RIGHT OUTER JOIN departments d ON d.department_id = e.department_id
GROUP BY GROUPING SETS( (d.department_name, j.job_title), (j.job_title,e.manager_id), ());
Thank you for your help!
Your queries differ because in the first one you outer join table departments, whereas in the second you outer join jobs and employees. (Which is why I don't like right outer joins so much. I don't find them very readable.) You take employees, join with jobs and then by RIGHT OUTER JOIN you say: and give me all departments anyhow, so give me additional (outer joined records) for jobs and employees. The second statement is equal to:
SELECT d.department_name AS "dept_name",
j.job_title AS "job_title",
e.manager_id AS "manager_id",
MAX(e.salary) AS "max_salary",
SUM(e.salary) AS "sum_salary"
FROM departments d
LEFT OUTER JOIN employees e ON e.department_id = d.department_id
LEFT OUTER JOIN jobs j ON j.job_id = e.job_id
GROUP BY GROUPING SETS( (d.department_name, j.job_title), (j.job_title,e.manager_id), ());
In old Oracle syntax you would write:
SELECT d.department_name "dept_name",
j.job_title "job_title",
e.manager_id "manager_id",
MAX(e.salary) "max_salary",
SUM(e.salary) "sum_salary"
FROM employees e, jobs j, departments d
WHERE e.department_id(+) = d.department_id
AND j.job_id(+) = e.job_id;
In old-style joins the syntax
FROM employees e, departments d
WHERE d.department_id(+) = e.department_id
is a left join and is equal to:
FROM employees e
LEFT JOIN departments d ON d.department_id = e.department_id
For these queries two work exactly the same, you should also change the RIGHT JOIN to LEFT JOIN in the second query, or d.department_id(+) = e.department_id to d.department_id = e.department_id(+) in the first.