Trace a Hierarchy in a Table - sql

I have an "Employee" table with an "EmployeeID" column and a column representing Employee's
Boss (BossID) which in turn is an employee in the "Employee" table. How can I trace the hierarchy from a given "EmployeeID" to the top most Boss. I do not want a self join approach in this, also I am using SQL Server 2005.
Thank you
Manu

You have to use some sort of self join basically with the table structure you describe but can use a recursive CTE for this to handle arbitrary depths of hierarchy if that was the concern?
WITH cte AS
(
SELECT EmployeeID, BossId
FROM Employee where EmployeeID = #EmployeeID
UNION ALL
SELECT e.EmployeeID, e.BossId
FROM Employee e JOIN cte ON cte.BossId = e.EmployeeID
)
SELECT EmployeeID
FROM cte

Related

Microsoft SMSS - Joining tables on t2.primary key = t1.foreign_key

I need to join two tables together to create a table with columns for employee id, employee name and their boss' name.
The 'hier' table
The 'employees' table
The query I wrote is almost working, putting an employee name in the right spot, but not the right employee:
SELECT em.emp_id, em.emp_name, em.emp_name AS boss_name
FROM employees em
LEFT JOIN hier h ON (h.boss_id = em.emp_name)
Which outputs:
I need to have each person's boss to have the right name, and in the case of Big Boss, 'N/A'. Like so:
You need a self join with Employee table
SELECT em.emp_id, em.emp_name, e1.emp_name AS boss_name
FROM employees em
LEFT JOIN employees em1 ON em.boss_id = em1.emp_id

Using output of one select query as input for another

I have an employee table which includes ManagerID as a foreign key. To get the currently logged ManagerId on Employees, I use a statement like -
SELECT MgrId FROM Employees WHERE EmpId=#EmpId
#EmpId is currently Logged on Employees.
I need to get the currently logged on employees manager and the manager's manager in one statement. This involves using the output of the above statement as input of another select statement. I am unsure how to do this. Any help is much appreciated.
Assuming your Employee ID field is named Id
SELECT e.Id [EmployeeId], e.MgrId [ManagerId], m.MgrId [ManagerManagerId]
FROM Employees e
LEFT JOIN Employees m ON e.MgrId = m.Id
WHERE e.EmpId=#EmpId

How to get result in following scenario

I have table
EMP(id int primary key, name varchar2(15), mgrID int).
Now this table contain all employees(including worker and manager) in company. mgrID column contain id of employee to whom they are reporting.
I want to list the name of worker who is not manager along with their name of manager.
What to do for such query.
I tried nested select query as follows:
select name, (select name from EMP where mgerID is NULL)
as Manager from EMP;
Will this query give proper result?
You could use a self-join:
SELECT e.name AS name, m.name AS manager_name
FROM emp e
JOIN emp m ON e.mgrid = m.id
Your query should fail because you sub-query is uncorrelated and will return multiple results if you have multiple top-level managers.
select name
, (select name from EMP b where b.ID = a.mgerID ) as Manager
from EMP a;
I think the self-join is the more canonical solution, but you should understand the correlated subquery as well as it has many application.

SQL Server 2005 UDF to make a table data-type of self referencing table data

I have a typical self-referencing table of employees.
How can you build an UDF to return a table data-type that can be used in other queries to join to such that I pass the UDF a id of a user in the table and get a result of that user's id and all users linked to him via a managerId?
The table I have is EmployeeId, ManagerID, Name....
All I need is to pass an EmployeeId and get a recursive result of all records who's ManagerID is the id of the passed in param, and any of the records that have these as managers, and so on...
thanks
CREATE FUNCTION GetEmployees
(
#EmployeeId int
)
RETURNS TABLE
AS
RETURN
(
WITH yourcte AS
(
SELECT EmployeeId, ManagerID, Name
FROM Employees
WHERE EmployeeId = #EmployeeId
UNION ALL
SELECT e.EmployeeId, e.ManagerID, e.Name
FROM Employees e
JOIN yourcte y ON e.ManagerID = y.EmployeeId
)
SELECT EmployeeId, ManagerID, Name
FROM yourcte
)

SQL Conditional JOIN

I have three tables 'Employees', 'Departments' and 'EmployeesInDepartments'
The 'Employees' tables references the 'Departments' table (Each employee must have a DepartmentId). However, an employee can exist in multiple departments by adding entries (EmployeeId and DepartmentId) to 'EmployeeInDepartments' table.
I currently have the following stored procedure to retrieve employees by departmentId:
CREATE PROCEDURE dbo.CollectEmployeesByDepartmentId
(
#DepartmentId int,
#IsDeleted bit
)
AS
BEGIN
SELECT Employees.*
FROM Employees
WHERE ((Employees.IsDeleted = #IsDeleted )
AND ((Employees.DepartmentId = #DepartmentId)
OR (Employees.EmployeeId IN (SELECT EmployeesInDepartments.EmployeeId
FROM EmployeesInDepartments
WHERE (EmployeesInDepartments.DepartmentId = #DepartmentId)
)
)
)
)
END
How can I optimize this stored procedure and possibly use JOINS?
My first recommendation to you is to remove department Id from the employee table. Insert all records to the employees in Departments table.
Then it's a simple inner join.
And of course, never use select * in production code.
Here's my re-write of your query:
WITH summary AS (
SELECT e.*
FROM EMPLOYEES e
WHERE e.isdeleted = #IsDeleted
AND e.parentid = 0)
SELECT a.*
FROM summary a
WHERE a.departmentid = #DepartmentId
UNION
SELECT b.*
FROM summary b
JOIN EMPLOYEESINDEPARTMENTS ed ON ed.employeeid = b.employeeid
AND ed.departmentid = #DepartmentId
The UNION is necessary to remove duplicates - if you know there'll never be duplicates, change UNION to UNION ALL.
The CTE called "summary" doesn't provide any performance benefit, it's just shorthand.
SELECT E.*
FROM Employees E
Left Join EmployeesInDepartments EID ON E.EmployeeId = EID.EmployeeId
And E.DepartmentId <> #DepartmentId
WHERE E.IsDeleted = #IsDeleted
And
(
E.DepartmentId = #DepartmentId
Or (EID.DepartmentId = #DepartmentId)
)
Edit to include IsDeleted logic.
I do agree with some of the other answers, your design should probably be changed. But this query should do it. If you have duplicates in EmployeesInDepartments, you can change that to a Select Distinct.
I would suggest you change the IN clause used in your query to WHERE EXISTS.
If you are using IN in your query it means you are not taking benifits of the indexes defined on the table, and the query will perform a complete table scan which impacts the query performance.
You can check this thread to convert a IN to WHERE EXISTS:
Changing IN to EXISTS in SQL