parent details in same row conditionally - sql

I have a table Called EmployeeLevel
Id Position ShouldApproveLeave
1000 1 0
2000 2 1
3000 3 0
4000 4 0
Note that the bool column ShouldApproveLeave is true for only one row
I have a table Employee with the following structure with LevelId as foreign key to EmployeeLevel
Id ParentId Name LevelId
1000 NULL Jack 1000
2000 1000 John 2000
3000 2000 Nick 3000
4000 3000 James 4000
I need a query to get employee details, position details as well as the manager responsible for his leave approval as below
query (id = 4000)
Id Name LevelPosition LeaveApprovalLevel LeaveApprovingManager
4000 James 4 2 John
query (id = 3000)
Id Name LevelPosition LeaveApprovalLevel LeaveApprovingManager
3000 Nick 3 2 John
query (id = 2000)
Id Name LevelPosition LeaveApprovalLevel LeaveApprovingManager
2000 John 2 2 John
Also I want to check leave approving manager only upwards in the hierarchy.
query (id = 1000)
Id Name LevelPosition LeaveApprovalLevel LeaveApprovingManager
1000 Jack 1 2 NULL
I wrote a CTE to get the manager with LeaveApproving bit set. Getting the employee with level is also straightforward. My issue is to get all these details in the same row.
So the question really is I got individual results for employee details and Leave Approving manager. How can I get this in a single row?
I managed to get individual queries for the following
employee details
Id ParentId Name Position
4000 3000 James 4
LeaveApprovalLevel details
Level
2
LeaveApprovingManager details
Id ParentId Name
2000 1000 John
How can I join these so that I will get this?
Id Name LevelPosition LeaveApprovalLevel LeaveApprovingManager
4000 James 4 2 John
Any tips are appreciated.

Here is one approach:
Get the hierarchy for the employee.
Use joins to get the information on the flag you want.
Use joins to bring back all the information you want.
As a query:
with cte as (
select e.id, e.parentid, 1 as lev
from employee e
where id = 4000
union all
select cte.id, e.parentid, lev + 1
from cte join
employee e
on e.id = cte.parentid
)
select e.*, el.*
from cte join
employee e
on cte.id = e.id join
employee el
on cte.parentid = el.id join
EmployeeLevel eml
on cte.parentid = eml.id;

try this
SELECT e1.Id, e1.Name, el1.Position as LevelPosition, el2.Position as LeaveApprovalLevel, e2.Name as LeaveApprovingManager FROM Employee e1 INNER JOIN EmployeeLevel el1 ON e1.LevelId = el1.Id LEFT JOIN Employee e2 ON e1.ParentId = e2.Id LEFT JOIN EmployeeLevel el2 ON e2.LevelId = el2.Id;
Here is the response
Check, here is the working Fiddle

Related

Displaying columns as rows SQL

I have the below tables:
Corporate table:
CorporateId DirectorId ManagerId SalesId
1 1 1 1
2 2 2 3
3 3 4 5
Employee table:
EmployeeId FirstName LastName
1 Tim Sarah
2 Tom Paulsen
3 Tam Margo
4 Eli Lot
5 Ziva Lit
I want to display, for one corporate,the names of the Director, Manager and Sales in rows. Example with corporate 3:
EmployeeId FirstName LastName
3 Tam Margo
4 Eli Lot
5 Ziva Lit
How can I do that? I know how to display rows as columns using pivot, but unsure if pivot can be used here also.
Any help please?
You may join the two tables as the following:
SELECT E.EmployeeId, E.FirstName, E.LastName
FROM Employee E JOIN Corporate C
ON E.EmployeeID IN (C.DirectorId ,C.ManagerId ,C.SalesId)
WHERE C.CorporateId=3
See a demo.
You can first un-pivot your rows into columns by using cross apply, after which you simply join the pivoted rows to your employee table:
select e.*
from corporate c
cross apply (
select EmployeeId from (
values (Directorid), (ManagerId), (SalesId)
)r(EmployeeId)
)r
join employee e on e.EmployeeId = r.EmployeeId
where c.CorporateId = 3;

How to select rows with related values?

I have a table "company" with data like follows containing employees and their companies:
EmployeeID EmployeeName CompanyName CompanyID ParentCoID
------------------------------------------------------------------------------------------------
100 John A 500 NULL
100 John A.1 600 500
250 Paul B 800 NULL
250 Paul ABC 2000 NULL
350 Joe D 1000 5000
600 Tom E 700 NULL
600 Tom E.2 1500 700
I am trying to display rows where if the Parent Co ID value is in the Company ID, then show both rows for that employee. So basically select employees that are in both the parent company and the sub company and show both of those rows.
I've tried
SELECT *
FROM company
WHERE CompanyID IN (SELECT ParentCoID FROM company)
but this only returns the row where the CompanyID value matches and I'm not sure how to also include that second row also.
My desired output for the sample above would be:
EmployeeID EmployeeName CompanyName CompanyID ParentCoID
------------------------------------------------------------------------------------------------
100 John A 500 NULL
100 John A.1 600 500
600 Tom E 700 NULL
600 Tom E.2 1500 700
As from the result above, Company A.1 is a sub company of A, and same with company E and E.2. I am trying to select employees that are in both the main company and sub company and therefore need to refer to the ParentCoID and the CompanyID columns.
SELECT * FROM company
WHERE EmployeeID IN (SELECT EmployeeID FROM company WHERE CompanyID IN (SELECT ParentCoID FROM company)
Please try this:
Updated the query.
SELECT * FROM company c
WHERE CompanyID IN (SELECT ParentCoID FROM company c2 WHERE c2.EmployeeID = c.EmployeeID) OR ParentCoID IN (SELECT CompanyID FROM company c3 WHERE c3.EmployeeID = c.EmployeeID)
Using EXISTS
SELECT a.*
FROM company a
WHERE EXISTS (SELECT b.parentcoid
FROM company b
WHERE a.companyid = b.parentcoid
AND a.employeeid = b.employeeid)
UNION ALL
SELECT c.*
FROM company c
WHERE EXISTS (SELECT d.companyid
FROM company d
WHERE d.companyid = c.parentcoid
AND d.employeeid = c.employeeid)
A recursive CTE is useful in querying hierarchical data.
with cte As (
select * from company c
Where ParentCoID is null and CompanyID = Any(Select ParentCoID From company Where EmployeeID=c.EmployeeID)
union all
select c.*
from cte p inner join company c On (p.CompanyID=c.ParentCoID And p.EmployeeID=c.EmployeeID)
)
select * From cte order by EmployeeID, CompanyID, ParentCoID
Here's a slightly different approach using a simple window function and a self-join and will likely be performant given a clustered index on (EmployeeId,ParentCoId).
with e as (
select EmployeeID,ParentCoID CoId,
Sum(case when ParentCoID is null then 1 end ) over(partition by EmployeeID) IsSub
from Company
)
select c.*
from e
join Company c on c.EmployeeID=e.EmployeeID
where (e.CoId=c.CompanyID or e.CoId=c.ParentCoID)
and e.CoId is not null and e.IsSub=1

Querying 100k records to 5 records

I have a requirement in such a way that it should join two tables with more than 100k records in one table and just 5 records in another table as shown below
Employee Dept Result
id Name deptid deptid Name Name deptid Name
1 Jane 1 1 Science Jane 1 Science
2 Jack 2 2 Maths Dane 1 Science
3 Dane 1 3 Biology Jack 2 Maths
4 Drack 3 4 Social Drack 3 Biology
5 Drim 5 Zoology Kery 4 Social
6 Drum 5 Drum 5 Zoology
7 Krack
8 Kery 4
.
.
100k
Which join need to be used to get the query in an better way to perform to get the result as shown.
I just want the query to join with other table from employee table only which has dept which i thought of below query but wanted to know is there any better way to do it.
Select e.name,d.deptid,d.Name from
(Select deptid,Name from Employee where deptid IS NOT NULL) A
and dept d where A.deptid=d.deptid;
Firstly not sure why you are performing your query the way you are. Should be more like
SELECT A.name, D.deptid,D.Name
FROM Employee A
INNER JOIN dept D
ON A.deptid = D.deptid
No need of the IS NOT NULL statement.
If this is a ONE TIME or OCCASIONAL thing and performance is key (not a permanent query in your DB) you can leave out the join altogether and do it using CASE:
SELECT
A.name, A.deptid,
CASE
WHEN A.deptid = 1 THEN "Science"
WHEN A.deptid = 2 THEN "Maths"
...[etc for the other 3 departments]...
END as Name
FROM Employee A
If this is to be permanent and performance is key, simply try applying an INDEX on the foreign key deptid in the Employee table and use my first query above.

Create view by joining three tables in SQL

I have three tables STUDENTS, SUBJECTS, RANK ,with data as -
1) STUDENTS [NAME(Primary)]
NAME
--------
Alex
Greg
2) SUBJECTS [ID(Primary)]:
ID
--------
100
101
102
3) RANK [SEQ(Primary), NAME, ID, RANK]
SEQ NAME ID RANK
------ ------- ------ ------
1 Alex 100 A
2 Greg 100 A
3 Greg 101 B
I want to create a view that should display data as
NAME ID RANK
------- ------ ------
Alex 100 A
Alex 101 Z
Alex 102 Z
Greg 100 A
Greg 101 B
Greg 102 Z
So, for every student and for every subject, the View should display the RANK if present in RANK table, else replace the NULL with 'Z'.
I'm a newbie to SQL. So any help in forming the query would be deeply appreciated!
cross join student and subject then left outer join the result with rank to get ranks for all (student, subject) combination. selecting column with NVL OR COALESCE will replace NULL with 'z'.
SELECT st.name,
su.id,
NVL(ra.rank,'Z') Rank, --COALESCE(ra.rank,'Z') Rank
FROM student st
CROSS JOIN subject su
LEFT OUTER JOIN rank ra
ON ra.name = st.name
AND ra.id = su.id
ORDER BY st.name,su.id
Note : ORDER BY can be removed from above query if you don't need.
fiddle
SELECT r.NAME, r.ID, NVL(r.RANK, 'Z')
FROM RANK r, studendts st, SUBJECTS su
WHERE st. NAME = r. NAME
AND su.ID = r.ID
ORDER BY 1,2,3

Select unique random posting/recruitment places of employees within a list of places

I am trying to select unique random posting/recruitment places of employees within a list of places, all the employees are already posted at the places, i am trying to generate a new random posting place for them with "where" condition that "employee new random location will not be equal to their home place"
Employee table is :
EmpNo Empname CurrentPosting Home Designation RandomPosting
1 Satish Kumar Samastipur Gazi Manager
2 Anil Kumar Singh Vaishali Patna Manager
3 Rajdev Prasad Nawada Gaya PO
4 Rajesh Kumar Sheikhpura Muzaffarpur PO
5 Jitendra Kumar Banka Bhagalpur Clerk
And so on...
And Places table is
PlaceID PlaceName Manager PO Clerk
1 Araria 2 0 1
2 Arwal 1 1 1
3 Aurangabad 1 0 2
4 Banka 2 1 1
5 Begusarai 1 1 1
6 Bhagalpur 1 1 2
7 Bhojpur 0 2 0
and so on...
i tried with rand() and newid() like as below,
select Employee.*, Place.PlaceName As RandomPosting from Employee
inner join Place on Place.PlaceID=Employee.EmpNo order by newid()
But unable to select what is required... that is to assign each Employee a PlaceName(from Place) randomly which is not equal to CurrentPosting and Home(in Employee).
Thanks in advance.
WITH cteCrossJoin AS (
SELECT e.*, p.PlaceName,
ROW_NUMBER() OVER(PARTITION BY e.EmpNo ORDER BY NEWID()) AS RowNum
FROM Employee e
CROSS JOIN Place p
WHERE e.Home <> p.PlaceName
)
SELECT *
FROM cteCrossJoin
WHERE RowNum = 1;