I have the following query:
select distinct p.title, e.first_name, e.last_name, max(e.salary)
from employees as e
inner join employees_projects as ep
on e.id = ep.employee_id
inner join projects as p
on p.id = ep.project_id
group by 1,2,3
order by p.title
Which returns multiple rows per title. I only want the max salary for each title.
title | first_name | last_name | max
--------------------------+------------+-----------+-------
Build a cool site | Cailin | Ninson | 30000
Build a cool site | Ian | Peterson | 80000
Build a cool site | Mike | Peterson | 20000
Design 3 New Silly Walks | Ava | Muffinson | 10000
Update TPS Reports | John | Smith | 20000
Tweaked #zealous code and this works:
select
title,
first_name,
last_name,
salary
from
(select
distinct p.title,
e.first_name,
e.last_name,
e.salary,
dense_rank() over (partition by p.title order by e.salary desc) as rnk
from employees as e
inner join employees_projects as ep
on e.id = ep.employee_id
inner join projects as p
on p.id = ep.project_id
group by 1,2,3, 4
) t
where rnk = 1
order by title
Try this window function dense_rank(). If there is tie in salary then it will return both records with max salary.
If you just want one record with max salary then use row_number().
select
title,
first_name,
last_name,
salary
from
(select
distinct p.title,
e.first_name,
e.last_name,
salary,
dense_rank() over (partition by title order by salary desc) as rnk
from employees as e
inner join employees_projects as ep
on e.id = ep.employee_id
inner join projects as p
on p.id = ep.project_id
group by 1,2,3
) t
where rnk = 1
order by title
Assuming you want one row per title, then use distinct on:
select distinct on (p.title) p.title, e.first_name, e.last_name, e.salary
from employees e join
employees_projects ep
on e.id = ep.employee_id join
projects p
on p.id = ep.project_id
order by title, salary desc;
If you can have multiple titles, then you can use rank() or dense_rank() in a subquery:
select title, first_name, last_name, salary
from (select p.title, e.first_name, e.last_name, e.salary,
rank() over (partition by p.title order by e.salary desc) as seqnum
from employees e join
employees_projects ep
on e.id = ep.employee_id join
projects p
on p.id = ep.project_id
) p
where seqnum = 1
order by title;
This is basically the case when group by cant be used meaning you wanna group with no loss of all columns too.
You could simply use below
Select * from (
select distinct p.title, e.first_name,
e.last_name, e.salary
, rank()
Over(partition by 1 order by
e.salary desc)
) rn
from employees as e
inner join employees_projects as ep
on e.id = ep.employee_id
inner join projects as p
on p.id = ep.project_id)
Where rn=1
Related
I want to SELECT one parent and all the child under it. What Oracle does is it keeps showing the parents against its child.
Please tell me how to show the following query in ORACLE from this:
select d.department_name, e.last_name from employees e join departments d
on (d.department_id=e.department_id)
group by d.department_name, e.last_name
order by 1;
DEPARTMENT_NAME LAST_NAME
------------------------------ ------------
Accounting Gietz
Accounting Higgins
Executive De Haan
Executive King
Executive Kochhar
-------------------------------------------
Like this:
DEPARTMENT_NAME LAST_NAME
------------------------------ ------------
Accounting Gietz
Higgins
Executive De Haan
King
Kochhar
-------------------------------------------
You can use analytical function as following:
Select case when rn = 1 then department_name end as dept
,last_name from
(select d.department_name, e.last_name,
Row_number() over (partition by d.department_name order by e.last_name) as rn
from employees e join departments d
on (d.department_id=e.department_id)
group by d.department_name, e.last_name)
order by department_name, rn;
Cheers!!
This type of data transformation is best done at the application layer. But if you want to do it in SQL, you can use window functions -- but with the caveat that the ordering of the rows is very important. I would suggest:
select (case when row_number() over (partition by d.department_id order by e.last_name) = 1
then d.department_name
end) as dept,
last_name
from employees e join
departments d
on d.department_id = e.department_id
group by d.department_name, e.last_name
order by d.department_name, dept nulls last;
I also sincerely doubt that you need the group by, unless the tables have duplicates. So:
select (case when row_number() over (partition by d.department_id order by e.last_name) = 1
then d.department_name
end) as dept,
last_name
from employees e join
departments d
on d.department_id = e.department_id
order by d.department_name, dept nulls last;
I have the following query to get the primary (main) and secondary function for each employee :
with
employeeScopeFunctions as (
select e.employeeId,
es.FunctionId,
ef.Label,
c.CompanyName,
realOrder = row_number() over(
partition by e.employeeId
order by isnull(es.sortOrder, 9999)
)
from employee e
LEFT JOIN employee_scope es on es.employeeId = e.employeeId
LEFT JOIN employee_function ef on es.FunctionId = ef.FunctionId
LEFT JOIN Company c ON es.CompanyId = c.ID
WHERE e.EmployeeId=54
)
select *,
primacy = iif(realOrder = 1, 'main', 'secondary')
from employeeScopeFunctions
For the EmployeeId=54 the result is like below :
EmployeeId FunctionId Label CompanyName realOrder Primacy
54 273 Group Chief Executive Officer C1 1 primary
54 273 Group Chief Executive Officer C2 2 secondary
54 273 Group Chief Executive Officer X5 3 secondary
54 897 Group Regional Chief Executive Officer X6 4 secondary
54 897 Group Regional Chief Executive Officer F6 5 secondary
54 39 Director VY 6 secondary
54 39 Director G7 7 secondary
What I want to get is regroup all the companies for a specific function and get three levels of primacy :
EmployeeId FunctionId Label CompanyName Primacy
54 273 Group Chief Executive Officer C1,C2,X5 primary
54 897 Group Regional Chief Executive Officer X6,F6 secondary
54 39 Director VY,G7 tertiary
If I followed you correctly, you could keep your existing CTE and turn on aggregation in the main query. ROW_NUMBER() can be used to rank the records by increasing realOrder.
This should work in SQL-Server:
WITH employeeScopeFunctions as (
SELECT
e.employeeId,
es.FunctionId,
ef.Label,
c.CompanyName,
realOrder = row_number() over(partition by e.employeeId order by isnull(es.sortOrder, 9999))
FROM
employee e
LEFT JOIN employee_scope es ON es.employeeId = e.employeeId
LEFT JOIN employee_function ef ON es.FunctionId = ef.FunctionId
LEFT JOIN company c ON es.CompanyId = c.ID
WHERE e.EmployeeId=54
)
SELECT
employeeId,
FunctionId,
Label,
CompanyName = STRING_AGG(CompanyName, ',') WITHIN GROUP (ORDER BY realOrder),
Primacy = CASE ROW_NUMBER() OVER(ORDER BY MIN(realOrder))
WHEN 1 THEN 'primary'
WHEN 2 THEN 'secondary'
WHEN 3 THEN 'tertiary'
END
FROM employeeScopeFunctions
GROUP BY
employeeId,
FunctionId,
Label
Alternative solution with an additional level of nesting to avoid nesting window function and aggregation:
WITH employeeScopeFunctions as (
SELECT
e.employeeId,
es.FunctionId,
ef.Label,
c.CompanyName,
realOrder = row_number() over(partition by e.employeeId order by isnull(es.sortOrder, 9999))
FROM
employee e
LEFT JOIN employee_scope es ON es.employeeId = e.employeeId
LEFT JOIN employee_function ef ON es.FunctionId = ef.FunctionId
LEFT JOIN company c ON es.CompanyId = c.ID
WHERE e.EmployeeId=54
)
SELECT
employeeId,
FunctionId,
Label,
CompanyName,
Primacy = CASE ROW_NUMBER() OVER(ORDER BY minRealOrder)
WHEN 1 THEN 'primary'
WHEN 2 THEN 'secondary'
WHEN 3 THEN 'tertiary'
END
FROM (
SELECT
employeeId,
FunctionId,
Label,
CompanyName = STRING_AGG(CompanyName, ',') WITHIN GROUP (ORDER BY realOrder),
minRealOrder = MIN(realOrder)
FROM employeeScopeFunctions
GROUP BY
employeeId,
FunctionId,
Label
) x
I have two tables. One for employees
LAST_NAME SALARY DEPARTMENT_ID
------------------------- ---------- -------------
Vargas 2500 50
Zlotkey 10500 50
Abel 11000 80
Taylor 8600 80
One for department name
DEPARTMENT_ID DEPARTMENT_NAME
------------- ------------------------------
50 Shipping
80 Sales
I want to select the top three employees who have the max salary in the employees table.After get, I want to get their department_name. Resulting like this.
LAST_NAME SALARY DEPARTMENT_NAME
------------------------- ---------- -------------
Abel 11000 Sales
Zlotkey 10500 Shipping
Taylor 8600 Sales
I had try this:
SELECT last_name, salary, department_id, ROWNUM as RANK
FROM (SELECT last_name, salary, department_id
FROM employees
ORDER BY salary DESC)
WHERE ROWNUM <= 3;
But i don't know how to use join on to get department_name.
Platform: windows10
SQLDeveloper version: 18.01
You can do this using window functions:
select e.last_name, e.salary, d.department_name
from (select e.*, max(sum_salary) over () as max_sum_salary
from (select e.*, sum(e.salary) over (partition by department_id) as sum_salary
from employees e
) e
) e join
department d
on e.department_id = d.department_id
where max_sum_salary = sum_salary
try this
select top(3)LAST_NAME, SALARY, DEPARTMENT_NAME
from employees e
inner join department d on e.DEPARTMENT_ID = d.DEPARTMENT_ID
where d.DEPARTMENT_NAME = 'sales'
order by SALARY desc
You can do JOIN with subquery :
SELECT e.LAST_NAME, e.SALARY, d.DEPARTMENT_NAME
FROM employees e INNER JOIN
department d
ON d.DEPARTMENT_ID = e.DEPARTMENT_ID
WHERE e.DEPARTMENT_ID = (SELECT e1.DEPARTMENT_ID
FROM employees e1
ORDER BY e1.SALARY DESC
FETCH FIRST 1 ROWS ONLY
);
EDIT : If you want only three employees then you can do :
SELECT e.LAST_NAME, e.SALARY, d.DEPARTMENT_NAME
FROM employees e INNER JOIN
department d
ON d.DEPARTMENT_ID = e.DEPARTMENT_ID
ORDER BY e.SALARY DESC
FETCH FIRST 3 ROWS ONLY
[ID] [Name] [Dept]
--------------------
1 Manu A
2 Anu A
3 Tanu A
4 Danu A
5 Anu B
6 Danu B
7 Danu C
8 Anu C
9 Tanu C
10 John C
11 Anu D
12 Jane D
13 Danu D
I need to get the Dept with maximum number of employees.
Here is what I tried
SELECT
ID, Name, Dept
FROM
(SELECT
*,
rn = ROW_NUMBER() OVER(PARTITION BY Dept)
FROM Emp) t
WHERE
rn = (SELECT MAX(rn) FROM t)
I need help in the WHERE clause.
You need aggregation to count the number of employees. The approach using row_number() is one approach, but with the right query:
SELECT Dept
FROM (SELECT Dept, COUNT(*) as cnt,
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) as seqnum
FROM Emp
) e
WHERE seqnum = 1;
However, a more common approach would just use ORDER BY and TOP:
SELECT TOP (1) Dept, COUNT(*) as cnt
FROM emp
GROUP BY Dept
ORDER BY COUNT(*) DESC;
If you wanted ties, then you would use WITH TIES in the SELECT.
Selecting all departments having the same max number of employees:
;WITH c -- create a list of depts and number of emp's
AS (SELECT deptid,
Count(*) cnt
FROM emp
GROUP BY deptid)
SELECT d.*
FROM dept d
INNER JOIN c
ON d.deptid = c.deptid
WHERE c.cnt = (SELECT Max(cnt)
FROM c)
No WHERE is needed. Try using a GROUP BY
SELECT COUNT(Name) as NameCount, Dept from Table
GROUP BY Dept
ORDER BY COUNT(Name) DESC
The biggest group(s) will be at the top
Results
NameCount | Dept
4 A
4 C
3 D
2 B
Ok, now you added the table structure:
;WITH c
AS (SELECT Dept,
Count(*) cnt
FROM Emp
GROUP BY Dept)
SELECT c.*
FROM c
WHERE c.cnt = (SELECT Max(cnt)
FROM c)
I can't quite figure your table structure, but this selects the department with the most employees
SELECT * from Dept WHERE deptid = (
SELECT TOP 1 deptid FROM employees
GROUP BY deptid
ORDER BY COUNT(*) DESC
)
I have two tables, JobTable and EmployeeTable with the following data:
EmployeeTable:
EmpId Salary
1 10
2 20
3 30
4 40
5 50
6 60
JobTable:
JobId EmpId
A 1
A 2
B 3
B 4
C 5
C 6
I need an SQL statement that will return the EmpId of the Employee with the minimum salary for each Job.
You could use the RANK() function like this:
WITH ranked AS (
SELECT
j.JobId,
e.EmpId,
e.Salary,
RANK() OVER (PARTITION BY j.JobId ORDER BY e.Salary) AS rnk
FROM JobTable j
INNER JOIN EmployeeTable e ON j.EmpId = e.EmpId
)
SELECT
JobId,
EmpId,
Salary,
FROM ranked
WHERE rnk = 1
Hmm, give this one a shot:
SELECT st.EmpID, min(st.salary)
FROM SalaryTable st INNER JOIN JobTable jt ON st.EmpID=jt.EmpID
WHERE jt.JobID = 'A'
GROUP BY st.EmpID