SQL MAX((SUM)) combined usage - sql

I have two tables:
EMPLOYEE: ID|DEPARTMENT_ID|CHIEF_ID|NAME|SALARY
DEPARTMENT: ID|NAME
The task here is to get list of departments with total max salary of all employees.
The query I'm trying to use (completes with no results):
SELECT s.DEPARTMENT_ID, s.SALARY_SUM
FROM (SELECT DEPARTMENT_ID, SUM(SALARY) SALARY_SUM, w.ID
FROM EMPLOYEE e
JOIN DEPARTMENT w ON w.ID = e.DEPARTMENT_ID
GROUP BY e.DEPARTMENT_ID, w.ID) s
WHERE s.SALARY_SUM = (SELECT MAX(SALARY) MaxSum
FROM EMPLOYEE w1
WHERE w1.ID = s.ID)

This will get you all the Deoatnent that have the highest Sum from their employees
CREATE tABLe EMPLOYEE(DEPARTMENT_ID INT, SALARY INT)
GO
CREATE TABLe DEPARTMENT(ID int)
GO
WITH CTE as (SELECT DEPARTMENT_ID, SUM(SALARY) SALARY_SUM, w.ID
FROM EMPLOYEE e
JOIN DEPARTMENT w ON w.ID = e.DEPARTMENT_ID
GROUP BY e.DEPARTMENT_ID, w.ID)
SELECT s.DEPARTMENT_ID, s.SALARY_SUM
FROM CTE s
WHERE s.SALARY_SUM = (SELECT MAX(SALARY_SUM) MaxSum
FROM CTE w1 )
GO
DEPARTMENT_ID | SALARY_SUM
------------: | ---------:
db<>fiddle here

You essentially have 4 data sets here.
EMPLOYEE e
DEPARTMENT w
Subquery1 s
Subquery2 (where clause)
Data set 3 has salary aggregated by Department ID.
Data set 4 has the largest employee salary for each department.
The Where clause is saying compare 3 to 4 where SALARY_SUM = MaxSum. Because SALARY_SUM is aggregated by Department ID, and MaxSum is the largest individual salary, this where clause will only return results for departments with 1 person (or if people have a salary of $0).
If a department has multiple people with salaries, the SUM of the department's salary will always be greater than the largest individual salary in the department.

Related

Simple SQL query not working out

click here for database model
Asked: Show for every department with at least 3 employees, the department's name and the amount of employees in that department born before 1967.
My code so far:
`Select department, department_name, numberofemployeesbefore1967 = ( select count(empleyee_id) from employee where year(dateofbirth) < 1967)
From employee inner join department on (department = department_id)
group by department, department_name
having count(*) >=3`
The output I have now: output
I feel like this is a really easy one, but I cannot find how to show only the employees born before 1967 for that specific department.
Anyone to help me out?
I check the year in the main query and >=3 in a subquery
SELECT department department_name, count(*)
FROM department d
JOIN employee e on d.department_id = e.department
WHERE YEAR(dateOfBirth) < 1967
AND (SELECT COUNT(*) FROM employee WHERE department = d.department_id) >= 3
GROUP BY d.id, d.name
Tweaked the subquery like so...
Select department, department_name, (select count(empleyee_id) from employee where year(dateofbirth) < 1967) AS numberofemployeesbefore1967
From employee inner join department on (department = department_id)
group by department, department_name
having count(*) >=3

Get MAX element based on two different tables

I have problem with SQL query on Oracle DB.. I have following tables:
DEPARTMENT(`ID` NUMBER(11), `NAME` VARCHAR(25))
EMPLOYEE(`ID` INT(11), `LASTNAME` VARCHAR(25), `DEP_ID` INT(11));
SALARIES(`ID` INT(11), `EMPLOYEE_ID` INT(11), `SALARY` INT(11));
Now, I want to get name of depratment with highest average sum of salary. Department isn't directly related to Salaries so probably I need to use Employee table as well.
I've created a query:
SELECT NAME, (SELECT SUM(SALARIES.SALARY) FROM SALARIES JOIN EMPLOYEE ON EMPLOYEE.EMPLOYEE_ID = EMPLOYEE.ID WHERE EMPLOYEE.DEP_ID = DEPARTMENT.ID GROUP BY EMPLOYEE.ID) AS AVG_OF_SUM FROM DEPARTMENT;
It returns list of department's name and avg sum. But now I need to get only one department name for the highest averange row.
Is my query actually OK? Or can be improved? And how can I get only one record?
Thanks for any help.
Regards,
D
Make use of the ANALYTIC function SUM...OVER
In the subquery, apply the analytic function, and then select only those rows which you desire.
For example,
SELECT DISTINCT DEPT, SUM(SAL) OVER (PARTITION BY DEPT ORDER_BY DEPT) SUM_SAL
FROM EMPLOYEE
ORDER_BY DEPT;
SELECT NAME, (SELECT MAX(SUM(SALARIES.SALARY))
FROM SALARIES
JOIN EMPLOYEE ON EMPLOYEE.EMPLOYEE_ID = EMPLOYEE.ID )
WHERE EMPLOYEE.DEP_ID = DEPARTMENT.ID
GROUP BY EMPLOYEE.ID) AS AVG_OF_SUM FROM DEPARTMENT;
SELECT NAME, avg_sal FROM
(SELECT d.NAME, avg(s.SALARY) avg_sal
FROM SALARIES s
JOIN EMPLOYEE e ON s.EMPLOYEE_ID = e.ID
JOIN DEPARTMENT d ON e.DEP_ID = d.ID
GROUP BY d.NAME
ORDER BY 2 DESC)
WHERE rownum = 1;
(This query shows a department with the highest avg salary. If you need sum replace AVG -> SUM)

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

SQL Query List Those that are higher than average

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))

Extra Fields with SQL MIN() & GROUP BY

When using the SQL MIN() function, along with GROUP BY, will any additional columns (not the MIN column, or one of the GROUP BY columns) match the data in the matching MIN row?
For example, given a table with department names, employee names, and salary:
SELECT MIN(e.salary), e.* FROM employee e GROUP BY department
Obviously I'll get two good columns, the minimum salary and the department. Will the employee name (and any other employee fields) be from the same row? Namely the row with the MIN(salary)?
I know there could very possibly be two employees with the same (and lowest) salary, but all I'm concerned with (now) is getting all the information on the (or a single) cheapest employee.
Would this select the cheapest salesman?
SELECT min(salary), e.* FROM employee e WHERE department = 'sales'
Essentially, can I be sure that the data returned along with the MIN() function will matches the (or a single) record with that minimum value?
If the database matters, I'm working with MySql.
If you wanted to get the "cheapest" employee in each department you would have two choices off the top of my head:
SELECT
E.* -- Don't actually use *, list out all of your columns
FROM
Employees E
INNER JOIN
(
SELECT
department,
MIN(salary) AS min_salary
FROM
Employees
GROUP BY
department
) AS SQ ON
SQ.department = E.department AND
SQ.min_salary = E.salary
Or you can use:
SELECT
E.*
FROM
Employees E1
LEFT OUTER JOIN Employees E2 ON
E2.department = E1.department AND
E2.salary < E1.salary
WHERE
E2.employee_id IS NULL -- You can use any NOT NULL column here
The second statement works by effectively saying, show me all employees where you can't find another employee in the same department with a lower salary.
In both cases, if two or more employees have equal salaries that are the minimum you will get them both (all).
SELECT e.*
FROM employee e
WHERE e.id =
(
SELECT id
FROM employee ei
WHERE ei.department = 'sales'
ORDER BY
e.salary
LIMIT 1
)
To get values for each department, use:
SELECT e.*
FROM department d
LEFT JOIN
employee e
ON e.id =
(
SELECT id
FROM employee ei
WHERE ei.department = d.id
ORDER BY
e.salary
LIMIT 1
)
To get values only for those departments that have employees, use:
SELECT e.*
FROM (
SELECT DISTINCT eo.department
FROM employee eo
) d
JOIN
employee e
ON e.id =
(
SELECT id
FROM employee ei
WHERE ei.department = d.department
ORDER BY
e.salary
LIMIT 1
)
Of course, having an index on (department, salary) will greatly improve all three queries.
The fastest solution:
SET #dep := '';
SELECT * FROM (
SELECT * FROM `employee` ORDER BY `department`, `salary`
) AS t WHERE IF ( #dep = t.`department`, FALSE, ( #dep := t.`department` ) OR TRUE );
Another approach can be using Analytical functions. Here is the query using analytical and ROW_NUM functions
select first_name, salary from (select first_name,salary, Row_NUMBER() over (PARTITION BY DEPARTMENT_ID ORDER BY salary ASC) as row_count from employees) where row_count=1;