SQL Oracle - optimum query - sql

I have three tables with the following schemas:
Persons(Person_id, Department_id, Sure_name, Name, Birthyear, Height, Manager_id)
Departments (Department_id, Department_Name, Code)
Salaries (Salary_id, Person_id, Salary)
I need to run a query which will display the name of the department, for which the difference between the workers' minimum and maximum heights is the greatest.
I have done it the following way:
select Department_Name
from Departments
where Department_id = (select Department_id
from Departments
join Persons
using (Department_id)
group by Department_id
having max(height) - min(height) = (select max(max(height) - min(height))
from Departments
join Persons
using (Department_id)
group by Department_id));
And it works fine, just I'm not really sure if the solution is optimum, there are two nested queries here, I wonder if I could achieve the same in a simpler way.

Try this.
SELECT Department_Name
FROM Departments D
INNER JOIN (SELECT Department_id,
Max(height) - Min(height) AS diff
FROM Departments
JOIN Persons
ON using (Department_id)
WHERE ROWNUM = 1
GROUP BY Department_id
ORDER BY diff DESC) B
ON d.Department_id = b.Department_id
or use Window Function
SELECT Department_id,
Department_Name
FROM (SELECT Row_number()OVER(ORDER BY Max(height)- Min(height) desc ) rn,
Department_id,
Department_Name
FROM Departments
JOIN Persons
ON using(Department_id)
GROUP BY Department_id,
Department_Name)a
WHERE rn = 1

This might work for you, a bit complicated but using the analytic function RANK() probably simplifies things a bit over using nothing but aggregates:
SELECT d.department_id, d.department_name, d.code, p1.height_diff
FROM departments d INNER JOIN (
SELECT department_id, height_diff, RANK() OVER ( ORDER BY height_diff DESC ) AS rn
FROM (
SELECT department_id, MAX(height) - MIN(height) AS height_diff
FROM Persons
GROUP BY department_id
)
) p1
ON d.department_id = p1.department_id
WHERE p1.rn = 1;
Please see SQL Fiddle demo here. N.B. I had previously ordered incorrectly, now corrected to DESC.

Related

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

Why this select selects no rows

I am trying to select a highest earner in 'IT' department, but I keep selecting no rows.
Salary and names are in table called employees while department_name is in a table named departments.
Can anyone please explain why does this select no rows and how should I do it?
SELECT first_name, last_name, salary, department_name
FROM employees
JOIN departments on departments.department_id = employees.department_id
WHERE salary = (SELECT max(salary) FROM employees WHERE department_name = 'IT');
Why this select selects no rows?
Your query fails because there is no column department_name in the employees table. So your subquery does not do what you expect:
where salary = (SELECT max(salary) FROM employees WHERE department_name = 'IT');
If you want to do this with a subquery, you need to correlate it:
select e.first_name, e.last_name, e.salary, d.department_name
from employees e
inner join departments d on d.department_id = e.department_id
where
d.department_name = 'IT'
and e.salary = (select max(e1.salary)
from employees e1
where e1.department_id = e.department_id);
You can use dense_rank if there are more than one highest earner for IT department.
select
first_name,
last_name,
salary,
department_name
from
(
select
first_name,
last_name,
salary,
department_name,
dense_rank() over (partition by department_name order by salary desc) as rnk
FROM employees e
JOIN departments d
on d.department_id = e.department_id
where department_name = 'IT'
) val
where rnk = 1
Use row_number() :
SELECT t.*
FROM (SELECT emp.first_name, emp.last_name, dempt.salary, dempt.department_name,
ROW_NUMBER() OVER(PARTITION BY dept.department_name ORDER BY dept.salary DESC) AS SEQ
FROM employees emp JOIN
departments dept
ON dept.department_id = emp.department_id
WHERE dempt.department_name = 'IT'
) t
WHERE seq = 1;
If salary has ties then you can use rank() instead.

The best way to find the Department with the maximum total salary Postgresql

Lets we have 2 standard tables Employees and Departments
CREATE TABLE departments (
id SERIAL PRIMARY KEY,
name VARCHAR
);
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
department_id INTEGER,
name VARCHAR,
salary NUMERIC(13,2)
);
What is the best way to find the name of the department with the maximum employees' total salary.
I've found two solutions and they looks too complicated for such simple task.
Using rank()
SELECT name FROM (
SELECT name, rank() OVER ( ORDER BY salary DESC ) AS rank
FROM (
SELECT
departments.name,
sum(salary) AS salary
FROM employees
JOIN departments ON department_id = departments.id
GROUP BY departments.name
) AS t1
) AS t2
WHERE rank = 1;
Using subquery
WITH t1 AS (SELECT
departments.name,
sum(salary) AS salary
FROM employees
JOIN departments ON departments.id = employees.department_id
GROUP BY departments.name
)
SELECT name FROM t1
WHERE t1.salary = (SELECT max(salary) FROM t1);
At first glance using rank should be less efficient as it performs unnecessary sorting. Though EXPLAIN shows that the first option is more efficient.
Or maybe someone suggests another solution.
So, what is the best way to find the Department with the maximum total salary using postgres?
I would write the rank() as:
SELECT *
FROM (SELECT d.name, SUM(e.salary) AS salary,
RANK() OVER (ORDER BY SUM(e.salary)) as rnk
FROM employees e JOIn
departments d
ON e.department_id = d.id
GROUP BY d.name
) d
WHERE rnk = 1;
(The additional subquery should not affect performance, but it adds nothing to clarify the query either.)
Because window functions are built-in to the database, the database has methods for making them more efficient. And there is overhead for getting the MAX() as well. But, to be honest, I would expect both methods to have similar performance.
I should note that if you want only one department returned -- even when there are ties -- then the simplest method is:
SELECT d.name, SUM(e.salary) AS salary
FROM employees e JOIn
departments d
ON e.department_id = d.id
GROUP BY d.name
ORDER BY SUM(e.salary) DESC
FETCH FIRST 1 ROW ONLY

Disconnected from the rest of the join graph

My task is to query a database, selecting the number of persons that work in the most numerous department. I do the following:
select count(*) from Persons join Departments using (Department_id)
where Department_id =
(select Department_id from Persons join Departments using (Department_id)
group by Department_id having count(*) =
(select max(count(*)) from Persons
join Departments using (Department_id) group by Department_id)
);
And it works fine, but I get a warning, that Persons is disconnected from the rest of the join graph. Is there something faulty with this solution? Or perhaps it could be done easier?
I'm not a fan of the max(count(*)) construct. It is an Oracle extension and it changes the semantics of group by (which in all other cases returns one row per group). How about just:
with d as (
select count(*) as cnt
from persons
group by department_id
)
select *
from d
where cnt = (select max(cnt) from d);
You are right, the join is in fact redundant. Here is my new proposed solution:
select count(*) from Persons
where Department_id is not null
group by Department_id
having count(*) = (select max(count(*)) from Persons
where Department_id is not null
group by Department_id);

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