SQL Server - Self Join - sql

I have a table as follows
EmployeeID Name ManagerID
2 David 3
3 Roger NULL
4 Marry 2
5 Joseph 2
7 Ben 2
Here Roger is Top Manager
Mike & David are Managers
And rest all are employees
I am looking for output like this:
EmployeeName ManagerName TopManager
Marry David Roger
Joseph David Roger
Ben David Roger
NULL David Roger
I tried using Self Join like:
SELECT e1.Name EmployeeName, ISNULL(e2.name, 'Top Manager') AS ManagerName
FROM Employee e1
LEFT JOIN Employee e2
ON e1.ManagerID = e2.EmployeeID
but it is not giving the output I am looking for.

If you can have different top managers, then a recursive CTE is needed:
with cte as (
select employeeid, name, name as topmanager
from Employee
where managerid is null
union all
select t.employeeid, t.name, cte.topmanager
from Employee t join
cte
on t.managerid = cte.employeeid
)
select *
from cte;
If there is only one top manager, then:
select e.*, topm.name as topmanager
from employee e cross join
(select e2.* from employee e2 where e2.managerid is null) as topm

I think you have to do self join twice to get the desired output.
I created the query in this way and got the output which you have mentioned. Please note that I did not include the last row which has null value, and rest the same as it is.
Query:
create table Employees (EmployeeID int, Name varchar(10), ManagerID int)
Insert into Employees values
(2, 'David' , 3 )
,(3, 'Roger' , NULL)
,(4, 'Marry' , 2 )
,(5, 'Joseph' , 2 )
,(7, 'Ben' , 2 )
select e.name as EmployeeName, e1.name ManagerName, e2.Name TopManager
from Employees e
left join Employees e1 on e.ManagerID = e1.employeeid
left join Employees e2 on e1.ManagerID = e2.EmployeeID
where e.ManagerID is not null and e1.ManagerID is not null
Where condition was given to restrict the manager names in Employee column.
Output:
EmployeeName ManagerName TopManager
Marry David Roger
Joseph David Roger
Ben David Roger

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;

T-SQL - Concatenate Multiple Rows

I am struggling with a query to return a list of managers with their respective employees
I have three tables as follows:
Managers
ManagerID ManagerName
1 Bob
2 Sally
3 Peter
4 George
EmployeeManager
EmployeeID ManagerID
1 1
1 1
2 2
2 2
3 3
3 3
4 4
4 4
Employees
EmployeeID EmployeeName
1 David
1 Joseph
2 Adam
2 Pete
3 Mark
3 Mavis
4 Susan
4 Jennifer
Desired Result Set
ManagerName CountEmployee Employees
Bob 2 David, Joseph
Sally 2 Anish, Pete
Peter 2 Mark, Mavis
George 2 Susan, Jennifer
The query I am currently using is as follows:
Select m.ManagerName
,Count(e.EmployeeName) Over(Partition By m.ManagerID) as CountEmployee
,Rank() Over(Partition By m.ManagerID Order By em.EmployeeID) [RankEmployee]
,e.EmployeeName
From dbo.Employees e
Left Join dbo.EmployeeManager em on em.ManagerID=e.ManagerID
Left Join dbo.Managers m on m.ManagerID=em.ManagerID;
This returns a list of managers and employees on individual rows but I'm struggling to concatenate the employee names as per the above table.
Any ideas or solutions?
Manpaal Singh
You can stuff the result to comma seperated result.
Select m.ManagerName
,Count(e.EmployeeName) Over(Partition By m.ManagerID) as CountEmployee
,Rank() Over(Partition By m.ManagerID Order By em.EmployeeID) [RankEmployee]
,STUFF((SELECT ',' + e.EmployeeName
FOR XML PATH('')), 1, 1, '') AS EmployeeName
From dbo.Employees e
Left Join dbo.EmployeeManager em on em.ManagerID=e.ManagerID
Left Join dbo.Managers m on m.ManagerID=em.ManagerID
SELECT M.ManagerName, E.EmployeeName
FROM Managers AS M
INNER JOIN EmployeeManager AS EM ON M.ManagerID = EM.ManagerID
INNER JOIN Employees AS E ON EM.EmployeeID = E.EmployeeID
ORDER BY M.ManagerName
This will give a list of managers and their employees.
when you fix the ID's of the employees table.
1 David
1 Joseph
2 Adam
2 Pete
3 Mark
3 Mavis
4 Susan
4 Jennifer
should be:
1 David
2 Joseph
3 Adam
4 Pete
5 Mark
6 Mavis
7 Susan
8 Jennifer
you can use recursive sql to convert rows in a string :
with t1 (mngId, empName) as (
select a.mngId,
b.empname
from manager as a, employee as b
where b.mngId = a.mngId),
t2 (mngID, nbr, empName, all_name) as (
select mngId,
cast(1 as Int),
min(empName),
cast(min(empName) as varchar(1000)
from t1
group by mngId
union all
select b.mngId,
b.nbr+1,
a.empName,
trim(b.all_name) concat ', ' concat a.empName
from t0 as a, t1 as b
where b.mngId = a.mngId
and a.empName > b.empName
and a.empName = (
select min( c.empName)
from t0 as c
where c.mngId = b.mngId
and c.empName > b.empName )
)
select *
from t1 as e
where nbr = (
select max(nbr)
from t1 as d
where d.mngId = e.mngId )

Retrieving MAX(date) value from joined table

I need help on a very basic SQL join concept, that I just can't seem to get right.
I have an employee table and a position table. The employee table is like so:
EmpID Name
1 Jane Jones
2 Bob Smith
3 Jim Adams
The position table is like this:
PosID EmpID Position DateFilled
1 1 Sales 1/2/2012
2 2 HR 4/5/2013
3 2 Mgmnt 6/1/2014
4 2 Sr. Mgmnt 7/5/2015
5 3 IT Support 4/6/2014
6 3 IT Devel. 5/11/2015
How can I get the following output:
EmpID Name Position DateFilled
1 Jane Jones Sales 1/2/2012
2 Bob Smith Sr. Mgmnt 7/5/2015
3 Jim Adams IT Devel. 5/11/2015
So, in other words, how do I join to get only the record with the max DateFilled column from the position table to join with the corresponding record in the employee table?
Any assistance would be greatly appreciated.
You can use ROW_NUMBER:
SELECT e.EmpID, e.Name, p.Position, p.DateFilled
FROM employee e
LEFT JOIN (
SELECT EmpID, Position, DateFilled,
ROW_NUMBER() OVER (PARTITION BY EmpID
ORDER BY DateFilled DESC) AS rn
FROM position
) p ON e.EmpID = p.EmpID AND p.rn = 1
You can do it using MAX() KEEP ( DENSE_RANK [FIRST|LAST] ... ) like this:
SELECT e.EmpId,
e.Name,
p.position,
p.datefilled
FROM employee e
INNER JOIN (
SELECT EmpID,
MAX( Position ) KEEP ( DENSE_RANK LAST ORDER BY DateFilled ) AS Position,
MAX( DateFilled ) AS DateFilled
FROM position
GROUP BY EmpID
) p
ON ( e.EmpId = p.EmpID );
try this
select temp.EmpID,(select position from Position where PosID =temp.PosID) position,DateFilled,Name from
(select EmpID,max(PosID) PosID,max(DateFilled) DateFilled
from position group by EmpID ) temp
inner join employee emp on emp.EmpID =temp.EmpID

CTE to replace JOIN

Can I use CTE instead of the my code here ? Here are the two tables and my code .
tbl1
D_ID department employee name manager name
112 d01 john d Peter k
113 d01 Emily f kevin s
tbl2
Emp_id employee name D_ID
100 john d 112
200 peter k 112
800 Emily f 113
500 kevin s 113
My code below , what I did is I joined tbl1 with tbl2 on D_ID and employee name and then filter out the records where employee's emp_id > manager's emp_id .
DECLARE #level nvarchar(MAX) =
(
select X.D_ID ,x.employee_NAME, x.emp_ID as employee_id,
y.manager_name ,y.emp_id as manager_id
) + ' '
from (
select distinct b.d_id , a.emp_id as employee_id
from tbl1 a , tbl2 b
where a.d_id=b.d_id and a.employee_NAME=b.employee_NAME
) x ,
(
select distinct b.d_id , a.emp_id as manager_id
from tbl1 a , tbl2 b
where a.d_id=b.d_id and a.employee_NAME=b.manager_NAME
) Y
where x.department=y.department and x.employee_id>=y.manager_id
FOR XML PATH('')
)
IF #level IS NOT NULL
BEGIN
RAISERROR(' employee ID>manager_id: %s',16, 1, #level)
with log;
END;
DESIRED OUTPUT is below as Emily f's employee id is > than her manager's ID .
D_ID employee_NAME employee_id manager_name manager_id
113 Emily f 800 kevin s 500
I think you just need some joins:
select t1.d_id, t1.employee_name, te.emp_id,
t1.employee_name as manager_name, tm.emp_id as manager_id
from tbl1 t1 join
tbl2 te
on t1.employee_name = te.employee_name join
tbl2 tm
on t1.manager_name = tm.employee_name
where te.emp_id > tm.emp_id;
It is quite curious that you are using the names to connect the two tables. Normally, you would use the employee id for this purpose and look up the name using the id.

Display the Employee Name (Boss) and number of Employee (Subordinates) in SQL

I have a table emp having foll data:
EmpID EmpName MgrID
100 King NULL
101 Smith 100
102 Shine 100
103 Racy 102
Now i want to Display the Employee Name (Boss) and number of Employee (Subordinates) something like this
BOSS SUBORDINATES
BLAKE 5
CLARK 1
FORD 1
JONES 2
KING 3
SCOTT 1
Please guide how to go about querying this table in SQL Server 2008.
Attempted query:
select e.first_name as ename,m.first_name as mname from employees e,employees m where e.manager_id=m.employee_id
Start by self-joining on EmpID=MgrID
Group by MgrID and EmpName
Select EmpName and count(*)
Translating this to SQL is mechanical:
SELECT b.EmpName, COUNT(*)
FROM Employee e
JOIN Employee b ON b.EmpID=e.MgrID
GROUP BY b.EmpID, b.EmpName
CREATE TABLE test (
EmpID INT,
EmpName VARCHAR(100),
MgrID INT)
INSERT INTO test VALUES (100, 'King', NULL),
(101, 'Smith', 100),
(102, 'Shine', 100),
(103, 'Racy', 102)
SELECT t1.EmpName AS Boss,
COUNT(*) AS Subordinates
FROM test AS t1 INNER JOIN test AS t2 ON t1.EmpID = t2.MgrID
GROUP BY t1.EmpName