Getting the primary (main), secondary and tertiary function for an emolyee - sql

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

Related

Only show one row per title in SQL query

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

Getting the primacy function for an emolyee

I have this following query to get the primacy for an employee :
with
employeeScopeFunctions as (
select e.employeeId,
es.FunctionId,
ef.Label,
c.CompanyName
,es.SortOrder
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 DISTINCT esf.EmployeeId, esf.FunctionId
,STUFF((SELECT ',' + CompanyName
FROM employeeScopeFunctions es
WHERE esf.EmployeeId=es.EmployeeId AND esf.FunctionId=es.FunctionId
FOR XML PATH('')),1,1,'') AS Companies
,SUM(esf.SortOrder) as sumOrder
FROM employeeScopeFunctions esf
GROUP BY esf.EmployeeId,esf.FunctionId
My output is like below :
EmployeeId FunctionId Label CompanyName sumOrder
54 39 Director C1,C2,X5 224
54 273 Group Chief Executive Officer X6,F6 66
54 897 Group Regional Chief Executive Officer VY,G7 130
What I want is to get primacy (primary,secondary, tertiary) to each function like below :
EmployeeId FunctionId Label CompanyName sumOrder primacy
54 39 Director C1,C2,X5 224 tertiary
54 273 Group Chief Executive Officer X6,F6 66 primary
54 897 Group Regional Chief Executive Officer VY,G7 130 secondary
The function having the minimal sumOrder will be the primary function and so on.
You can use a ROW_NUMBER() ordered by the SUM() result, then a CASE to display your custom string value.
;with employeeScopeFunctions as
(
SELECT
e.employeeId,
es.FunctionId,
ef.Label,
c.CompanyName,
es.SortOrder
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
),
RankingBySum AS
(
SELECT
esf.EmployeeId,
esf.FunctionId,
STUFF (
(
SELECT ',' + CompanyName
FROM employeeScopeFunctions es
WHERE esf.EmployeeId=es.EmployeeId AND esf.FunctionId=es.FunctionId
FOR XML PATH('')
)
, 1,1,'') AS Companies,
SUM(esf.SortOrder) as sumOrder,
-- Add this ranking here
SumRanking = ROW_NUMBER() OVER (PARTITION BY esf.EmployeeId ORDER BY SUM(esf.SortOrder) ASC)
FROM
employeeScopeFunctions esf
GROUP BY
esf.EmployeeId,
esf.FunctionId
)
SELECT
R.EmployeeId,
R.FunctionId,
R.Companies,
R.sumOrder,
-- Display your primacy condition here
primacy = CASE R.SumRanking
WHEN 1 THEN 'primary'
WHEN 2 THEN 'seconday'
WHEN 3 THEN 'tertiary' END
FROM
RankingBySum AS R

Join Tables and Return data in single row different columns with count of join

We have 3 tables in Oracle 11g, need to left join them and return the data in single row different columns with count of the join, Is there any way We can acheive the same.
Example:
Table1: (Employee_Data)
Table2: (Employee_Address)
Table3: (Employee_Role)
Expected Result:
Mack has 2 addresses and 2 roles so Emp_Addr_Count is 2, Emp_Role_Count is 2 and the related data is in same row different column.
Kindly note that EMP_ID is unique in Employee_Data table and Employee_Address and Employee_Role could be multiple or zero for a Employee.
Thanks in Advance.
Try this:
SELECT E.Emp_Id
,E.Emp_Name
,E.Emp_Age
,NVL(MAX(EA.RN),0)Addr_Count
,NVL(MAX(CASE WHEN EA.RN = 1 THEN EA.Emp_Address END),' ')Emp_Address_1
,NVL(MAX(CASE WHEN EA.RN = 1 THEN EA.Emp_City END),' ')Emp_City_1
,NVL(MAX(CASE WHEN EA.RN = 2 THEN EA.Emp_Address END),' ')Emp_Address_2
,NVL(MAX(CASE WHEN EA.RN = 2 THEN EA.Emp_City END),' ')Emp_City_2
,NVL(MAX(ER.RN1),0)Role_Count
,NVL(MAX(CASE WHEN ER.RN1 = 1 THEN ER.Emp_task END),' ')Emp_task_1
,NVL(MAX(CASE WHEN ER.RN1 = 2 THEN ER.Emp_task END),' ')Emp_task_2 FROM Employee_Data E JOIN(
SELECT Emp_Id
,ROW_NUMBER() OVER(PARTITION BY Emp_Id ORDER BY Emp_City desc) RN
,Emp_City
,Emp_Address
FROM Employee_Address
)EA ON EA.Emp_Id = E.Emp_Id left JOIN(
SELECT Emp_Id
,ROW_NUMBER() OVER(PARTITION BY Emp_Id ORDER BY Emp_Task) RN1
,Emp_task
FROM Employee_Role
)ER ON ER.Emp_Id = E.Emp_Id GROUP BY E.Emp_Id,E.Emp_Name,E.Emp_Age
Output:
EMP_ID EMP_NAME EMP_AGE ADDR_COUNT EMP_ADDRESS_1 EMP_CITY_1 EMP_ADDRESS_2 EMP_CITY_2 ROLE_COUNT EMP_TASK_1 EMP_TASK_2
1 MACK 45 2 HOME PARADISE MUM TINDER ONCLAVE DEL 2 Manage Task Resource Manage
2 JACK 30 1 BLUE PLAZA MUM 1 Code
3 ANGEL 27 1 HOME PARADISE MUM 0
You can join them as in the following statement :
WITH t AS
(
SELECT d.*, a.emp_address, a.emp_city, r.emp_task
FROM employee_data d
JOIN employee_address a on ( d.emp_id = a.emp_id )
FULL OUTER JOIN employee_role r on ( d.emp_id = r.emp_id )
)
SELECT emp_id, emp_name, emp_age, count(distinct emp_address) emp_addr_count,
min(emp_address) emp_address_1, max(emp_city) emp_city_1,
decode(min(emp_address),max(emp_address),null,max(emp_address)) emp_address_2,
decode(min(emp_city),max(emp_city),null,min(emp_city)) emp_city_2,
count(distinct emp_task) emp_role_count, min(emp_task) emp_task_1,
decode(min(emp_task),max(emp_task),null,max(emp_task)) emp_task_2
FROM t
GROUP BY emp_id, emp_name, emp_age
ORDER BY emp_id;
SQL Fiddle Demo

Inner join with one row of another table

**Table Employee**
Id Name
1 EmpName1
2 EmpName2
3 EmpName3
**Table EmpDeptHistory**
Id EmpId Dept Date
1 1 Housing 2015-03-02
2 2 Finance 2015-01-03
3 1 WareHouse 2015-05-02
4 2 Housing 2015-02-06
5 3 WareHouse 2015-02-02
6 1 Housing 2015-05-01
7 2 Finance 2015-01-02
8 2 Housing 2015-05-04
9 2 Finance 2015-05-02
10 1 WareHouse 2015-03-08
11 1 Housing 2015-02-20
I need find the recent dept with which every employee worked. Also I need to find for individual employee by passing EmpId
The following query returns only one employee and not all :(
SELECT e.id, edh.dept,edh.date
FROM Employee e
inner join (select top 1 eh.empid, eh.dept, eh.date
from EmpDeptHistory eh
order by eh.date desc) as edh
on e.id=edh.empid
yes, I understand the top 1 will give the emp id based on date, hence only one employee details is show. I am not sure how to get all the employee recent department.
select e.id,edh.dept,edh.date
from employee e
inner join EmpDeptHistory edh
on e.id = (Select eh.empid, eh.dept, eh.date
from EmpDeptHistory eh
where e.id=eh.empid
order by eh.date desc)
The above throws
The ORDER BY clause is invalid in views, inline functions, derived
tables, subqueries, and common table expressions, unless TOP,
OFFSET or FOR XML is also specified.
You can use CROSS APPLY to run the right-hand subquery once for each left-hand row:
SELECT e.id, edh.dept,edh.date
FROM Employee e cross apply ( select top 1 eh.empid, eh.dept, eh.date from
EmpDeptHistory eh where eh.empid = e.id order by eh.date desc) as edh
You can use a CTE and a ranking function like ROW_NUMBER:
WITH CTE AS
(
SELECT e.id, edh.dept, edh.date,
rn = ROW_NUMBER() OVER (PARTITION BY edh.EmpId ORDER BY edh.date DESC)
FROM Employee e inner join EmpDeptHistory edh
on e.id = edh.empid
)
SELECT id, dept, date
FROM CTE
WHERE rn = 1
DEMO
For the latest department for each employee, you can do it like so:
SELECT t1.*
FROM EmpDeptHistory t1 INNER JOIN
(
SELECT EmpId, MAX(Date) [Date]
FROM EmpDeptHistory
GROUP BY EmpId
) AS t2
ON t1.EmpId = t2.EmpId AND t1.Date = t2.Date
EmpId can be put into a where clause if needed.

How do I get a single row value in a group statement?

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