How to display deptno, deptname and no of employee? - sql

I am practicing my SQL skills with "EMP" and "Dept" table given in Oracle 11g
I am trying to display department no , department name and no of employees of the department where employee no is greater than 4?

You can group by the department's details and apply a having condition:
SELECT d.deptno, d.deptname, COUNT(*)
FROM dept d
JOIN emp e ON d.deptno = e.deptno
GROUP BY d.deptno, d.deptname
HAVING COUNT(*) > 4

Related

How to count the number of members of different departments after executing a JOIN between two tables?

I have a table Emp and another table Dept
This is table Emp
This is table Dept
The query I have to execute is to display the average salary (i.e., sal) for all departments (i.e. DeptName) with more than 5 working people. So we'll have to do a JOIN or something and match the DeptID here with the DeptID there etc.
This is really confusing and I don't understand how to go about it.
you can use group by with having clause query for that.
group by Dept_id and check count using having clause.
SELECT
SUM(E.Sal) / COUNT(E.Dept_id) AS Avg_Sal,
E.Dept_Id
FROM EMP E
JOIN Dept D
ON D.DeptId = E.Dept_Id
GROUP BY E.DeptId
HAVING COUNT(E.Dept_Id) > 5
Select
DeptName,
AVG(sal) as averageSalary,
Count(EmpId) as NumberofEE
FROM Emp e
/*Left Join Because there are Nulls in the Dept_Id each Emp should belong to
1 dept*/
LEFT JOIN Dept d on e.Dept_Id = d.DeptId
GROUP BY
DeptName
Having Count(EmpId)> 5
This will give you the average salary (i.e., sal) for all departments (i.e. DeptName) with more than 5 working people
If i'm right with your requirement.

Select departments with or without employees. Also select employees if assigned

I want to select DNAME, DEPTNO from all departments with or without employees assigned. And if a department does have any employees I want to get their ENAME, EMPNO.
I tried this: select e.empno,e.name,d.deptno,d.dname from emp e full join dept d
on e.deptno=d.deptno
But it didn't work.
Table structure:
DEPT
DEPTNO DNAME
EMP
EMPNO ENAME DEPTNO
Use OUTER JOIN(LEFT/RIGHT)
SELECT D.DEPTNO,
D.DNAME,
E.EMPNO,
E.ENAME
FROM DEPT D
LEFT OUTER JOIN EMP E
ON D.DEPTNO = E.DEPTNO
When the Department does not have any employee then NULL will be displayed in E.EMPNO and E.ENAME column
SELECT
DEPT.DEPTNO,DEPT.DNAME,EMP.EMPNO,EMP.NAME
FROM
DEPT LEFT OUTER JOIN
EMP ON DEPT.DEPTNO = EMP.DEPTNO

Display empno,ename,sal,deptno,dname,location, Display highest paid employee of every department

How to display highest paid employee of every department WITH empno,ename,sal,deptno,dname,location....
select e.empno,e.ename, max(e.Sal),d.Deptno
from DEPTNO d
inner join emp e
on d.Deptno = e.DEPTNO
group by d.DEPTNO;
Employee table:
Deptno table:
Using the row_number() analytic function makes this easy:
select e.empno,
e.ename,
e.sal,
d.deptno,
d.dname,
d.loc
from Deptno d
join (select e.*,
row_number() over (
partition by e.deptno
order by sal desc) as rn
from Employee e) e
on e.deptno = d.deptno
and e.rn = 1
In this case, the row_number() function will return a value of 1 for the highest paid employee of every department, so that's why the join condition includes e.rn = 1 to limit the results to those employees.
Try this :
select empno,ename,deptno,sal from
(
select e.empno,e.ename,d.Deptno,sal,max(sal) over (partition by d.deptno order by null) max_sal from Employee e,Deptno d where e.deptno = d.deptno
)where sal=max_sal
here max_sal return max salary with partition by department

the grouping column for an aggregate function is as a join condition?

I was reading some Oracle SQL resources and I found this SQL code:
SELECT e.ename AS "NAME",
e.sal AS "Salary",
e.deptno,
AVG(a.sal) dept_avg
FROM emp e, emp a
WHERE e.deptno = a.deptno
AND e.sal > ( SELECT AVG(sal)
FROM emp
WHERE deptno = e.deptno )
GROUP BY e.ename, e.sal, e.deptno;
This SQL code is supposed to return every employee that gets more than the average salary of his department and display his name, his salary his department's ID and then the average salary in his department.
In order to return the dept_avg, we have to group by deptno, but the grouping columns are weird. What I guess, is that the grouping column is the column that is used as a join condition, the a.deptno. Is that true ? if not can someone please clarify it?
Maybe re-writing with more modern conventions makes it clearer?
WITH avgbydept as
(
SELECT deptno, avg(sal) as avgsal
FROM emp
GROUP BY deptno
)
SELECT e.ename AS "NAME",
e.sal AS "Salary",
e.deptno,
AVG(a.sal) dept_avg
FROM emp e
JOIN emp a ON e.deptno = a.deptno
JOIN avgbydept abd ON e.deptno = abd.deptno
WHERE e.sal > abd.avgsal
GROUP BY e.ename, e.sal, e.deptno;
One thing this makes clear is that it has a "bug" of an extra join and group by -- To do as you say:
This SQL code is supposed to return every employee that gets more than
the average salary of his department and display his name, his salary
his department's ID and then the average salary in his department.
I believe you want this
WITH avgbydept as
(
SELECT deptno, avg(sal) as avgsal
FROM emp
GROUP BY deptno
)
SELECT e.ename AS "NAME",
e.sal AS "Salary",
e.deptno,
abd.avgsal as dept_avg
FROM emp e
JOIN avgbydept abd ON e.deptno = abd.deptno
WHERE e.sal > abd.avgsal
If you remove GROUP BY and use SELECT *, you'll see what's happening.
emp is joined on itself, every employee with salary higher than average is joined with every other employee in his department, making an awful lot of rows. Then, from that data, average salary (from every other worker in dept) is counted again, using GROUP BY. It's impressively inefficient, look at other answers to see how it should have been done.
GROUP BY can throw us for a loop. Here's an easy way to think about grouping:
select field1, field1, sum(field3)
from ..
group by <all fields that do not participate in aggregate>
The query you noticed could be re-written somewhat like this:
select e.*, t.avgsal
from emp e
inner join (select deptno, avg(sal) avgsal from emp group by deptno) t
on e.deptno = t.deptno
where e.sal > t.avgsal
Now you can see that the subquery aliased with t will get average salary by department. We then use departments to join employee and our derived avg salary by department and eliminate the need for grouping.

Employees with largest salary in department

I found a couple of SQL tasks on Hacker News today, however I am stuck on solving the second task in Postgres, which I'll describe here:
You have the following, simple table structure:
List the employees who have the biggest salary in their respective departments.
I set up an SQL Fiddle here for you to play with. It should return Terry Robinson, Laura White. Along with their names it should have their salary and department name.
Furthermore, I'd be curious to know of a query which would return Terry Robinsons (maximum salary from the Sales department) and Laura White (maximum salary in the Marketing department) and an empty row for the IT department, with null as the employee; explicitly stating that there are no employees (thus nobody with the highest salary) in that department.
Return one employee with the highest salary per dept.
Use DISTINCT ON for a much simpler and faster query that does all you are asking for:
SELECT DISTINCT ON (d.id)
d.id AS department_id, d.name AS department
,e.id AS employee_id, e.name AS employee, e.salary
FROM departments d
LEFT JOIN employees e ON e.department_id = d.id
ORDER BY d.id, e.salary DESC;
->SQLfiddle (for Postgres).
Also note the LEFT [OUTER] JOIN that keeps departments with no employees in the result.
This picks only one employee per department. If there are multiple sharing the highest salary, you can add more ORDER BY items to pick one in particular. Else, an arbitrary one is picked from peers.
If there are no employees, the department is still listed, with NULL values for employee columns.
You can simply add any columns you need in the SELECT list.
Find a detailed explanation, links and a benchmark for the technique in this related answer:
Select first row in each GROUP BY group?
Aside: It is an anti-pattern to use non-descriptive column names like name or id. Should be employee_id, employee etc.
Return all employees with the highest salary per dept.
Use the window function rank() (like #Scotch already posted, just simpler and faster):
SELECT d.name AS department, e.employee, e.salary
FROM departments d
LEFT JOIN (
SELECT name AS employee, salary, department_id
,rank() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rnk
FROM employees e
) e ON e.department_id = d.department_id AND e.rnk = 1;
Same result as with the above query with your example (which has no ties), just a bit slower.
This is with reference to your fiddle:
SELECT * -- or whatever is your columns list.
FROM employees e JOIN departments d ON e.Department_ID = d.id
WHERE (e.Department_ID, e.Salary) IN (SELECT Department_ID, MAX(Salary)
FROM employees
GROUP BY Department_ID)
EDIT :
As mentioned in a comment below, if you want to see the IT department also, with all NULL for the employee records, you can use the RIGHT JOIN and put the filter condition in the joining clause itself as follows:
SELECT e.name, e.salary, d.name -- or whatever is your columns list.
FROM employees e RIGHT JOIN departments d ON e.Department_ID = d.id
AND (e.Department_ID, e.Salary) IN (SELECT Department_ID, MAX(Salary)
FROM employees
GROUP BY Department_ID)
This is basically what you want. Rank() Over
SELECT ename ,
departments.name
FROM ( SELECT ename ,
dname
FROM ( SELECT employees.name as ename ,
departments.name as dname ,
rank() over (
PARTITION BY employees.department_id
ORDER BY employees.salary DESC
)
FROM Employees
JOIN Departments on employees.department_id = departments.id
) t
WHERE rank = 1
) s
RIGHT JOIN departments on s.dname = departments.name
Good old classic sql:
select e1.name, e1.salary, e1.department_id
from employees e1
where e1.salary=
(select maxsalary=max(e.salary) --, e. department_id
from employees e
where e.department_id = e1.department_id
group by e.department_id
)
Table1 is emp - empno, ename, sal, deptno
Table2 is dept - deptno, dname.
Query could be (includes ties & runs on 11.2g):
select e1.empno, e1.ename, e1.sal, e1.deptno as department
from emp e1
where e1.sal in
(SELECT max(sal) from emp e, dept d where e.deptno = d.deptno group by d.dname)
order by e1.deptno asc;
SELECT
e.first_name, d.department_name, e.salary
FROM
employees e
JOIN
departments d
ON
(e.department_id = d.department_id)
WHERE
e.first_name
IN
(SELECT TOP 2
first_name
FROM
employees
WHERE
department_id = d.department_id);
`select d.Name, e.Name, e.Salary from Employees e, Departments d,
(select DepartmentId as DeptId, max(Salary) as Salary
from Employees e
group by DepartmentId) m
where m.Salary = e.Salary
and m.DeptId = e.DepartmentId
and e.DepartmentId = d.DepartmentId`
The max salary of each department is computed in inner query using GROUP BY. And then select employees who satisfy those constraints.
Assuming Postgres
Return highest salary with employee details, assuming table name emp having employees department with dept_id
select e1.* from emp e1 inner join (select max(sal) avg_sal,dept_id from emp group by dept_id) as e2 on e1.dept_id=e2.dept_id and e1.sal=e2.avg_sal
Returns one or more people for each department with the highest salary:
SELECT result.Name Department, Employee2.Name Employee, result.salary Salary
FROM ( SELECT dept.name, dept.department_id, max(Employee1.salary) salary
FROM Departments dept
JOIN Employees Employee1 ON Employee1.department_id = dept.department_id
GROUP BY dept.name, dept.department_id ) result
JOIN Employees Employee2 ON Employee2.department_id = result.department_id
WHERE Employee2.salary = result.salary
SQL query:
select d.name,e.name,e.salary
from employees e, depts d
where e.dept_id = d.id
and (d.id,e.salary) in
(select dept_id,max(salary) from employees group by dept_id);
Take look at this solution
SELECT
MAX(E.SALARY),
E.NAME,
D.NAME as Department
FROM employees E
INNER JOIN DEPARTMENTS D ON D.ID = E.DEPARTMENT_ID
GROUP BY D.NAME