SQL Query List Those that are higher than average - sql

I have 2 tables in the following format:
employee (employeeID, EmployeeName, DepartmentID)
departments (DepartmentID, DeptName)
How many employees there are who work in each of the departments that have more employees than the average number of employees in a department.
im looking to the results in the following format:
Dept Name | Num of Emp
engineering | 10
science | 15

SELECT deptName, cnt
FROM (
SELECT departmentID, COUNT(*) AS cnt
FROM employee
GROUP BY
departmentID
HAVING COUNT(*) >=
(
SELECT AVG(cnt)
FROM (
SELECT COUNT(*) AS cnt
FROM employee
GROUP BY
departmentID
)
)
) e
JOIN departments d
ON d.departmentID = e.departmentID
In Oracle, you can use analytic functions which are more elegant:
SELECT DeptName, cnt
FROM (
SELECT q.*, AVG(cnt) OVER() AS acnt
FROM (
SELECT departmentID, COUNT(*) AS cnt
FROM employee
GROUP BY
departmentID
) q
) e
JOIN departments d
ON d.departmentID = e.departmentID
WHERE cnt >= acnt

since an employee can be in only one department, the average number of employees is just the total # of employees over the total number of departments. So how about:
SELECT dept.name, COUNT(emp.id) AS employeeCount
FROM emp INNER JOIN dept ON emp.deptId = dept.id
GROUP BY dept.name
HAVING (COUNT(emp.id) >
(SELECT COUNT(*) FROM emp) /
(SELECT COUNT(*) FROM dept))

Related

Retrieve the name of employee who is getting higher salary from each department

I have three different tables like department, employee and salary for SQL Server.
Its structure
CREATE TABLE department (DeptId INT,DeptName Varchar(100))
INSERT INTO department VALUES (1,'Accounts');
INSERT INTO department VALUES (2,'Package');
CREATE TABLE employee (EmpId INT,DeptId INT,EmpName varchar(20))
INSERT INTO employee VALUES (1,1,'Sachin');
INSERT INTO employee VALUES (2,1,'Vikas');
INSERT INTO employee VALUES (3,2,'Sikha');
INSERT INTO employee VALUES (4,2,'Disha');
CREATE TABLE salary(EmpId INT,Sal int)
INSERT INTO salary VALUES (1,400);
INSERT INTO salary VALUES (2,700);
INSERT INTO salary VALUES (3,700);
INSERT INTO salary VALUES (4,900);
Result will be:
DepName | EmpName
-----------------
Accounts | Vikas
Package | Disha
Please help me on this, I need a query to find the required result.
I have tried below query, but not able get EmpName.
select DeptName, max(Sal) as Salary from
(select dep.DeptName, emp.EmpName, sal.Sal from salary as sal
inner join employee emp on emp.EmpId = sal.EmpId
inner join department dep on dep.DeptId = emp.DeptId) as tbls
group by DeptName
You can use RANK() to query the salary.
Here is example:
select DeptName
,EmpName
from
(
select DeptName
,EmpName
,rank() over (partition by d.deptid order by sal desc) as ranknum
from employee as e
inner join salary as s
on e.EmpId=s.EmpId
left join department as d
on d.DeptId = e.DeptId
) as ranktable
where ranknum = 1
We can use CTE as well:
;WITH maxSalStaff AS (
SELECT
rnk = ROW_NUMBER() OVER (PARTITION BY d.DeptId ORDER BY s.Sal DESC),
d.DeptName,
e.EmpName,
s.Sal
--ItemID
FROM department d
INNER JOIN employee e ON e.DeptId = d.DeptId
INNER JOIN salary s ON s.EmpId = e.EmpId
)
SELECT * FROM maxSalStaff WHERE rnk = 1
try this
SELECT T.EmpName, D.DeptName
FROM
(
SELECT E.EmpId, E.DeptId, E.EmpName, S.Sal,
RANK() OVER (PARTITION BY E.DeptId ORDER BY S.Sal DESC)Rank
FROM employee E
INNER JOIN salary s ON E.EmpId = S.EmpId
)T
INNER JOIN department D ON (T.DeptId = D.DeptId)
WHERE T.Rank=1
Use DENSE_RANK() in case multiple people can have same salary. Like below
WITH cteRowNum AS (
select
e.EmpName,
d.DeptName,
sal,
DENSE_RANK() OVER (PARTITION BY DeptName order by sal desc) as RowNo
from employee e inner join
departments d on d.DeptId= e.DeptId inner join
salary s on e.EmpId= s.EmpId
)
SELECT EmpName, DeptName, Sal
FROM cteRowNum
WHERE RowNo = 1;

Department has Maximum Staff

Write a query to display the name of the department that has the maximum staff count order by department name.
This is the schema of my problem:
I tried this:
select d.department_name
from department d
inner join staff s on d.department_id=s.department_id
group by d.department_name
having count(s.staff_id) >= all
( select count(s.staff_id) as cnt
from department d
inner join staff s on d.department_id=s.department_id
group by department_name )
order by d.department_name desc;
and I was able to pass one test case which results in 'SE' department, but I wasn't able to pass another test case.I don't know what second testcase want. I am not sure what I have done wrong in my code above.
You can use the windows function as follows:
select department_name, cnt
from
(select department_name, rank() over (order by cnt desc) as rn, cnt
from
(select d.department_name, count(1) cnt
from department d inner join staff s
on d.department_id=s.department_id
group by d.department_name))
where rn = 1
order by department_name

Write a query to display the name of the department that has the maximum staff count order by department name

select max(count(department_id))
from staff
group by department_id
ERROR at line 4:
ORA-00918: column ambiguously defined
Select department_name
from staff s
inner join department d on s.department_id=d.department_id
having count(s.department_id) in (Select max(count(department_id))
from staff) group by department_id
--- Expected output ---
DEPARTMENT_NAME
------------------------------
SE
```none
---Difference in Output---
(select max(count(department_id)) from staff)group by departmentDEPARTMENT_idNAME
*------------------------------
SERROR at line 4:
ORA-00918: column ambiguously defined
Summary of tests
+------------------------------+
| 2 tests run / 0 test passed |
+------------------------------+
Query:
select dept.DEPARTMENT_NAME, count(dept.DEPARTMENT_NAME) as staff_count from
hr.departments dept, hr.employees emp
where dept.DEPARTMENT_ID=emp.DEPARTMENT_ID(+)
group by dept.DEPARTMENT_NAME
order by count(dept.DEPARTMENT_NAME) desc
FETCH FIRST 1 ROW ONLY;
You can run the above query in https://livesql.oracle.com/
And the for more information on Fetch can be found on link
https://oracle-base.com/articles/12c/row-limiting-clause-for-top-n-queries-12cr1
On multiple places, You missed alias and I have added it as following:
SELECT
D.DEPARTMENT_NAME -- ADDED ALIAS HERE
FROM
STAFF S
INNER JOIN DEPARTMENT D ON S.DEPARTMENT_ID = D.DEPARTMENT_ID
GROUP BY
D.DEPARTMENT_ID -- ADDED ALIAS HERE
HAVING
COUNT(S.DEPARTMENT_ID) IN (
SELECT
MAX(COUNT(DEPARTMENT_ID))
FROM
STAFF
);
Also, You can achieve the same result using the following query:
SELECT
D.DEPARTMENT_NAME
FROM
DEPARTMENT D
JOIN (
SELECT
S.DEPARTMENT_ID,
COUNT(1)
FROM
STAFF S
ORDER BY
2 DESC
FETCH FIRST ROWS ONLY
) S ON S.DEPARTMENT_ID = D.DEPARTMENT_ID;
Cheers!!
select department_id, count(*) from staff group by department_id
having count(*) = (select max(count(*))
from staff group by department_id )
check it out
select department_name from (select d.department_name,count(s.staff_id) as c from department d join staff s on d.department_id=s.department_id
group by d.department_name) mx where c=(select max(count(staff_id)) from staff group by department_id) order by department_name;
SELECT department_name
FROM department
WHERE department_id IN
(SELECT department_id
FROM staff
HAVING COUNT(department_id) IN
(SELECT MAX(COUNT(department_id))
FROM staff
GROUP BY department_id)
GROUP BY department_id);

Highest paid employee + average salary of the departments

An employee belongs to a department (foreign key = D_ID). An employee has a SSN (primary key), name, salary and D_ID.
A department has multiple employees (primary key = ID)
I want a query that returns Department name| Name of Highest Paid Employee of that department | His Salary | Average salary of employees working in the same department.
I know how to select the first part:
SELECT
D.name, E.name, E.salary
FROM
Employee E, Department D
WHERE
salary IN (SELECT MAX(E.salary)
FROM Employee E
GROUP BY E.D_ID)
AND E.D_ID = D.ID
I know also how to select the last part:
SELECT AVG(E.salary)
FROM Employee E
GROUP BY E.D_ID
How do I put these together in a single query?
You can use window functions for that:
select department_name, employee_name, salary, avg_dept_salary
from (
select e.name as employee_name,
d.name as department_name,
e.salary,
max(e.salary) over (partition by d.id) as max_dept_salary,
avg(e.salary) over (partition by d.id) as avg_dept_salary
from Employee E
join Department D on e.d_id = d.id
) t
where salary = max_dept_salary
order by department_name;
The above is standard ANSI SQL and runs on all modern DBMS.
I would do something like this:
SELECT d.name
, e.name
, e.salary
, n.avg_salary
FROM Department d
JOIN ( SELECT m.d_id
, MAX(m.salary) AS max_salary
, AVG(m.salary) AS avg_salary
FROM Employee m
GROUP BY m.d_id
) n
ON n.d_id = d.id
JOIN Employee E
ON e.d_id = d.id
AND e.salary = n.max_salary

SELECT specific information

My tables are structured like this (there are more values in the tables but I only wrote the ones relevant to this):
Department(dep_id, dep_name)
Employee(dep_id)
I need to display dep_name and the number of employees in every department, except once specific department (let's call it DepX) and only the departments with more than one employee.
I tried multiple methods to solve this but none of them worked.
Some methods I tried:
SELECT department.dep_name, COUNT(employee.dep_id) AS NumberOfEmployees FROM employee
INNER JOIN department ON employee.dep_id=department.dep_id
WHERE dep_name<>'DepX'
GROUP BY dep_id
HAVING COUNT(employee.dep_id) > 1;
SELECT dep_name FROM department
WHERE dep_name <>'DepX'
UNION
SELECT COUNT(*) FROM employee
WHERE COUNT(*) > 1
GROUP BY dep_id;
I can't figure this out. Thanks!
The first example does now work because you're including dep_name in your results without an aggregation but not grouping on it.
You can either use the department name in your grouping instead of the ID:
SELECT department.dep_name, COUNT(employee.dep_id) AS NumberOfEmployees FROM employee
INNER JOIN department ON employee.dep_id=department.dep_id
WHERE dep_name<>'DepX'
GROUP BY department.dep_name
HAVING COUNT(employee.dep_id) > 1;
or do the COUNT in a subquery:
SELECT department.dep_name,
e.NumberOfEmployees
FROM department
INNER JOIN (SELECT dep_id,
COUNT(*) NumberOfEmployees
FROM employee
GROUP BY dept_id
HAVING COUNT(dept_id) > 1
) e
ON department.dep_id = e.dep_id
WHERE dep_name<>'DepX'
SELECT department.dep_name, COUNT(employee.dep_id) AS NumberOfEmployees FROM employee
INNER JOIN department ON employee.dep_id=department.dep_id
WHERE department.dep_name not in('DepX')
GROUP BY department.dep_name
HAVING COUNT(employee.dep_id) > 1;
update your table alias per your need
TEST this. This query help you return not only dept_name, it can return all fields from Department if you want:
SELECT d.*, A.numOfEmployees
FROM Department d,
(
SELECT e.dep_id, COUNT(*) numOfEmployees
FROM Employee e
GROUP BY e.dep_id
HAVING COUNT(*) > 1
) A
WHERE d.dep_id = A.dep_id
AND d.dep_name != 'DepX'