SQL Server : CTE going a level back in the where clause - sql

I would like to set a limit to a recursive query. But I don't want to lose the last entry on which i limited the recursion.
Right now the query looks like this:
WITH MyCTE (EmployeeID, FirstName, LastName, ManagerID, level, SPECIAL)
AS (
SELECT a.EmployeeID, a.FirstName, a.LastName, a.ManagerID, 0 as Level
FROM MyEmployees as a
WHERE ManagerID IS NULL
UNION ALL
SELECT b.EmployeeID, b.FirstName, b.LastName, b.ManagerID, level + 1
FROM MyEmployees as b
INNER JOIN MyCTE ON b.ManagerID = MyCTE.EmployeeID
WHERE b.ManagerID IS NOT NULL and b.LastName != 'Welcker'
)
SELECT *
FROM MyCTE
OPTION (MAXRECURSION 25)
And it gets limited by the Manager Welcker, the result looks like this:
EmployeeID; FirstName; LastName; ManagerID; level
1 Ken Sánchez NULL 0
What I want is to have the employee 'Welcker' included. The problem is I only have the name of the last person I need to have on my list. There are some entries below in the command structure but I don't know them and I don't want to see them.
Here's a example of how I imagine the result of my query to look.
EmployeeID FirstName LastName ManagerID level
1 Ken Sánchez NULL 0
273 Brian Welcker 1 1
Any help would be very much appreciated

WITH MyCTE (EmployeeID, FirstName, LastName, ManagerID, level, NotMatched)
AS (
SELECT a.EmployeeID, a.FirstName, a.LastName, a.ManagerID, 0 as Level, 0 AS NotMatched
FROM MyEmployees as a
WHERE ManagerID IS NULL
UNION ALL
SELECT b.EmployeeID, b.FirstName, b.LastName, b.ManagerID, level + 1,
CASE WHEN (MyCTE.LastName = 'Welcker' AND MyCTE.level = 1) THEN 1 ELSE MyCTE.NotMatched END
FROM MyEmployees as b
INNER JOIN MyCTE ON b.ManagerID = MyCTE.EmployeeID
)
SELECT *
FROM MyCTE
WHERE NotMatched != 1

Can you just change that one line to b.LastName = 'Welcker'?

select distinct
t.b_eid as empId,
t.b_name as name,
case t.b_mid when 0 then null else t.b_mid end as managerId
--convert(varchar,t.b_eid) + ', ' +
--t.b_name + ', ' +
--case t.b_mid when '0' then 'null' else convert(varchar,t.b_mid) end
--as bvals
from(
select
coalesce(a.mid, 0) as a_mid,
a.eid as a_eid,
a.name as a_name,
coalesce(b.mid, 0) as b_mid,
b.eid as b_eid,
b.name as b_name
from
(values(null,1,'adam'),(null,2,'barb'),(201,3,'chris')) as a(mid,eid,name) cross join
(values(null,1,'adam'),(null,2,'barb'),(201,3,'chris')) as b(mid,eid,name)
) as t
where (t.a_mid = t.b_eid or t.a_mid = 0 or t.b_mid = 0) and t.a_name != 'chris'

Related

Displaying the difference between rows in the same table

I have a table named Employee_audit with following schema,
emp_audit_id
eid
name
salary
1
1
Daniel
1000
2
1
Dani
1000
3
1
Danny
3000
My goal is to write a SQL query which will return in following format, considering the first row also as changed value from null.
columnName
oldValue
newValue
name
null
Daniel
salary
null
1000
name
Daniel
Dani
name
Dani
Danny
salary
1000
3000
I have written the below SQL query,
WITH cte AS
(
SELECT empid,
name,
salary,
rn=ROW_NUMBER()OVER(PARTITION BY empid ORDER BY emp_audit_id)
FROM Employee_audit
)
SELECT oldname=CASE WHEN c1.Name=c2.Name THEN '' ELSE C1.Name END,
newname=CASE WHEN c1.Name=c2.Name THEN '' ELSE C2.Name END,
oldsalary=CASE WHEN c1.salary=c2.salary THEN NULL ELSE C1.salary END,
newsalary=CASE WHEN c1.salary=c2.salary THEN NULL ELSE C2.salary END
FROM cte c1 INNER JOIN cte c2
ON c1.empid=c2.empid AND c2.RN=c1.RN + 1
But it gives the result in following format
oldname
newname
oldsalary
newsalary
Daniel
Dani
null
null
Dani
Danny
1000
3000
Could you please answer me, how can I get the required result.
The lead and lag functions are to help you out.
The "diffs" calculates differences for each column you need to find diff to
with diffs as (
select 'name' colName, emp_audit_id, eid, lag(name, 1, null) over (partition by eid order by emp_audit_id) oldValue, name newValue
from some_table
union all
select 'salary', emp_audit_id, eid, cast(lag(salary, 1, null) over (partition by eid order by emp_audit_id) as varchar), cast(salary as varchar) newValue
from some_table
)
select *
from diffs
where oldValue <> newValue or oldValue is null
order by emp_audit_id, eid
If you give each row a row number in a CTE then join on yourself to the next row you can compare the old and the new values. Unioning the 2 different column names is a bit clunky however, if you needed a more robust solution you might look at pivoting the data.
You also obviously have to convert all values to a common datatype e.g. a string.
declare #Test table (emp_audit_id int, eid int, [name] varchar(32), salary money);
insert into #Test (emp_audit_id, eid, [name], salary)
values
(1, 1, 'Daniel', 1000),
(2, 1, 'Dani', 1000),
(3, 1, 'Danny', 3000);
with cte as (
select emp_audit_id, eid, [name], salary
, row_number() over (partition by eid order by emp_audit_id) rn
from #Test
)
select C.emp_audit_id, 'name' columnName, P.[Name] oldValue, C.[name] newValue
from cte C
left join cte P on P.eid = C.eid and P.rn + 1 = C.rn
where coalesce(C.[name],'') != coalesce(P.[Name],'')
union all
select C.emp_audit_id, 'salary' columnName, convert(varchar(21),P.salary), convert(varchar(21),C.salary)
from cte C
left join cte P on P.eid = C.eid and P.rn + 1 = C.rn
where coalesce(C.salary,0) != coalesce(P.salary,0)
order by C.emp_audit_id, columnName;
Returns:
emp_audit_id
columnName
oldValue
newValue
1
name
NULL
Daniel
1
salary
NULL
1000.00
2
name
Daniel
Dani
3
name
Dani
Danny
3
salary
1000.00
3000.00
I highly encourage you to add DDL+DML (as show above) to all your future questions as it makes it much easier for people to assist.

IIF with ISNULL?

SELECT first_name, last_name
(SELECT ISNULL(IIF(getdate() between vacation_start and vacation_end, 1, 0),0) from vacatoin_request where mat_emp = W.mat_emp) as is_on_vacation,
(SELECT ISNULL(IIF(getdate() between mission_start and mission_end, 1, 0),0) from mission_order where mat_emp = W.mat_emp) as is_on_mission
FROM
workers W
IIF is working fine but when I add ISNULL, It's still returning null If that worker have no vacation requests or mission orders in the other tables.
The problem is the select returns NULL so ISNULL needs to be outside the select
SELECT first_name, last_name
ISNULL((SELECT TOP 1 IIF(getdate() between vacation_start and vacation_end, 1, 0) from vacatoin_request where mat_emp = W.mat_emp),0) as is_on_vacation,
ISNULL((SELECT TOP 1 IIF(getdate() between mission_start and mission_end, 1, 0) from mission_order where mat_emp = W.mat_emp),0) as is_on_mission
FROM
workers W
SELECT
first_name, last_name
CASE WHEN v.mat_emp IS NOT NULL THEN 1 ELSE 0 END as is_on_vacation
CASE WHEN m.mat_emp IS NOT NULL THEN 1 ELSE 0 END as is_on_mission
FROM
workers W
LEFT JOIN
vacatoin_request v
ON
v.mat_emp = W.mat_emp AND
getdate() between v.vacation_start and v.vacation_end
LEFT JOIN
mission_order m
ON
m.mat_emp = W.mat_emp AND
getdate() between m.mission_start and m.mission_end
SELECT first_name, last_name
(SELECT COUNT(DISTINCT v.mat_empt) from vacatoin_request v where v.mat_emp = W.mat_emp and getdate() between vacation_start and vacation_end) as is_on_vacation,
(SELECT COUNT(DISTINCT m.mat_emp) from mission_order m where m.mat_emp = W.mat_emp and getdate() between mission_start and mission_end) as is_on_mission
FROM
workers W

How to collapse similar rows in SQL query

Using an MS SQL query, I would like to collapse redundant data in sequential rows to make a report easier to read. Suppose I have the following data:
ID | Department | Name | Task
-------------------------------------------------
1 Sales Mike Call customer
2 Sales Mike Create quote
3 Sales Sarah Create order
4 Engineering Sam Design prototype
5 Engineering Sam Calculate loads
6 Production Team1 Build parts
7 Production Team2 Build parts
8 Production Team1 Assemble parts
9 Accounting Amy Invoice job
10 Sales Mike Call customer
11 Sales Mike Follow up after 30 days
How can I get the following summarized rows from my query:
Sales: Mike (Call customer, Create quote) Sarah (Create order)
Engineering: Sam (Design prototype, Calculate loads)
Production: Team1 (Build parts) Team2 (Build parts) Team1 (Assemble parts)
Accounting: Amy (Invoice job)
Sales: Mike (Call customer, Follow up after 30 days)
Essentially if the previous department and name are the same, add the task to a comma separated sub-list. If only the department is the same, start a new named sub-list and if the department is new then start a new row. Notice that items 10 and 11 should not be included in the first row so that the order of tasks is not lost.
In C# this task is easy using loops and/or Linq, however, I now need to produce the same result from a SQL query.
Use recursive Common Table Expressions (working SQLFiddle example)
;with RecursivePersonTask( Id, Department, Name, Tasks )
as
(
select
a.Id
, a.Department
, a.Name
, a.Task
from
dbo.Task a
left outer join dbo.Task b
on a.Department = b.Department
and a.Name = b.Name
and a.Id = b.Id + 1
where
b.Id is null
union all
select
t.Id
, t.Department
, t.Name
, rpt.Tasks + ', ' + t.Task
from
RecursivePersonTask rpt
inner join dbo.Task t
on rpt.Department = t.Department
and rpt.Name = t.Name
and rpt.Id = t.Id - 1
)
, CombinedPersonTasks( Id, Department, Name, Tasks )
as
(
select
ROW_NUMBER() over ( order by a.Id )
, a.Department
, a.Name
, '(' + a.Tasks + ')'
from
RecursivePersonTask a
left outer join RecursivePersonTask b
on a.Department = b.Department
and a.Name = b.Name
and a.Id = b.Id - 1
where
b.Id is null
)
, RecursiveDepartmentTasks( Id, Department, Tasks )
as
(
select
a.Id
, a.Department
, a.Name + ' ' + a.Tasks
from
CombinedPersonTasks a
left outer join CombinedPersonTasks b
on a.Department = b.Department
and a.Id = b.Id + 1
where
b.Id is null
union all
select
cpt.Id
, cpt.Department
, rdt.Tasks + ' ' + cpt.Name + ' ' + cpt.Tasks
from
RecursiveDepartmentTasks rdt
inner join CombinedPersonTasks cpt
on rdt.Department = cpt.Department
and rdt.Id = cpt.Id - 1
)
, CombinedDepartmentTasks( Id, Department, Tasks )
as
(
select
ROW_NUMBER() over ( order by a.Id )
, a.Department
, a.Tasks
from
RecursiveDepartmentTasks a
left outer join RecursiveDepartmentTasks b
on a.Department = b.Department
and a.Id = b.Id - 1
where
b.Id is null
)
select
*
from
CombinedDepartmentTasks
order by
Id
If you have SQL Server 2012, you can use the ROWS parameters of the windowing functions:
with tasks as (
select 1 as id, 'Sales' as dept ,'Mike' as name, 'Call customer' as task union
select 2, 'Sales' ,'Mike', 'Create quote' union
select 3, 'Sales' ,'Sarah', ' Create order' union
select 4, 'Engineering' ,'Sam', ' Design prototype' union
select 5, 'Engineering' ,'Sam', ' Calculate loads' union
select 6, 'Production' ,'Team1', ' Build parts' union
select 7, 'Production' ,'Team2', ' Build parts' union
select 8, 'Production' ,'Team1', ' Assemble parts' union
select 9, 'Accounting' ,'Amy', ' Invoice job' union
select 10, 'Sales' ,'Mike', ' Call customer' union
select 11, 'Sales' ,'Mike', ' Follow up after 30 days'
)
select max(NewDepartmentRollover) over (order by id rows unbounded preceding) as ColumnToGroupOn , * from (
select
case when max(dept) over (order by id rows between 1 preceding and 1 preceding) = dept then NULL else id end as NewDepartmentRollover,
* from tasks
) GroupOn
If you only have SQL Server 2005 or 2008, something like this will do:
with tasks as (
select 1 as id, 'Sales' as dept ,'Mike' as name, 'Call customer' as task union
select 2, 'Sales' ,'Mike', 'Create quote' union
select 3, 'Sales' ,'Sarah', ' Create order' union
select 4, 'Engineering' ,'Sam', ' Design prototype' union
select 5, 'Engineering' ,'Sam', ' Calculate loads' union
select 6, 'Production' ,'Team1', ' Build parts' union
select 7, 'Production' ,'Team2', ' Build parts' union
select 8, 'Production' ,'Team1', ' Assemble parts' union
select 9, 'Accounting' ,'Amy', ' Invoice job' union
select 10, 'Sales' ,'Mike', ' Call customer' union
select 11, 'Sales' ,'Mike', ' Follow up after 30 days'
)
, b as (
select
row_number() over (order by tasks.id) as rn,
tasks.id as lefty
from tasks
left join tasks t3
on t3.id = tasks.id - 1
where tasks.dept <> isnull(t3.dept,'')
)
select tasks.*, lefty as columnToGroupOn from tasks left join (
select b.lefty, isnull(b2.lefty,999)-1 as righty from b
left join
b b2 on b.rn = b2.rn - 1
) c
on tasks.id between lefty and righty

recursive sql function with rollup logic?

i have a SQL that using a recursive CTE to expand a self-referancing employees table builds a result set of defects aggregated by user and severity level.
here is my CTE:
ALTER FUNCTION [dbo].[fnGetEmployeeHierarchyByUsername]
(
#NTID varchar(100) = null
)
RETURNS TABLE
AS
RETURN
(
WITH yourcte AS
(
SELECT EmployeeId, ManagerNTID, ManagerID, NTID, FullName--, Name
FROM Employees
WHERE NTID = #NTID
UNION ALL
SELECT e.EmployeeId, e.ManagerNTID, e.ManagerID, e.NTID, e.FullName--, e.Name
FROM Employees e
JOIN yourcte y ON e.ManagerNTID = y.NTID
)
SELECT EmployeeId, ManagerID, NTID, FullName--, Name
FROM yourcte
)
here is my SQL for aggregating defects by the user:
SELECT e.FullName, Urgent, High, Medium, Low
FROM fnGetEmployeeHierarchyByUsername ('ssalvati') e
LEFT OUTER JOIN(
SELECT [AssignedTo],
SUM([1-Urgent]) AS Urgent,
SUM([2-High]) AS High,
SUM([3-Medium]) AS Medium,
SUM([4-Low]) AS Low
FROM (SELECT [AssignedTo],[BusinessSeverity] FROM Defects WHERE Status <> 'Closed') D
PIVOT (COUNT([BusinessSeverity]) FOR [BusinessSeverity] IN ([1-Urgent],[2-High],[3-Medium],[4-Low])) V
GROUP BY [AssignedTo]) AS def
ON e.ntid = def.[AssignedTo]
i want to have a porc that takes a username as a param and generates a result like the SQL above but with 2 enhancements:
i need it to list the user passed in as a param to be listed as the first record of the result-set.
i need the employees that report into the manager to show only one level deep and not show the full tree. the first level should be a roll up of all the underlying defects assigned to people who roll up into all the level one users. in other words i dont want to show a entire tree under the manager like it is now, i need it to show only one level deep but with a sum of defects for all the levels.
ideas?
This isn't tested as I don't have a mssql install here nor your data, but, I think it should be generally right and at least push you in a useful direction.
First, you need to change the query in your UDF to give two additional pieces of information. The "topmost" employee for your aggregation collapsing (which I think you said is the first direct report, not the very top employee), and the overall depth. As such:
WITH yourcte AS
(
SELECT EmployeeId, ManagerNTID, ManagerID, NTID, FullName, 0 as Depth, ntid as Topmost
FROM Employees
WHERE NTID = #NTID
UNION ALL
SELECT e.EmployeeId, e.ManagerNTID, e.ManagerID, e.NTID, e.FullName, y.Depth+1, case when y.depth = 0 then e.ntid else y.Topmost end
FROM Employees e
JOIN yourcte y ON e.ManagerNTID = y.NTID
)
SELECT EmployeeId, ManagerID, NTID, FullName, Depth, Topmost
FROM yourcte
Then, your actual query needs a few extra details to extract that information and use it
SELECT
e.FullName,
Urgent,
High,
Medium,
Low
FROM fnGetEmployeeHierarchyByUsername ('ssalvati') e
LEFT OUTER JOIN(
SELECT [AssignedTo],
SUM([1-Urgent]) AS Urgent,
SUM([2-High]) AS High,
SUM([3-Medium]) AS Medium,
SUM([4-Low]) AS Low
FROM (SELECT [AssignedTo],[BusinessSeverity] FROM Defects WHERE Status <> 'Closed') D
join fnGetEmployeeHierarchyByUsername ('ssalvati') e2 on d.AssignedTo = e2.ntid
PIVOT (COUNT([BusinessSeverity]) FOR [BusinessSeverity] IN ([1-Urgent],[2-High],[3-Medium],[4-Low])) V
where e2.TopMost = e.ntid
GROUP BY [AssignedTo]) AS def
ON e.ntid = def.[AssignedTo]
where e.Depth <= 1
The double call to your UDF might be a bit expensive, so you may want to consider putting this into a sproc and using a temp table to catch the results of the UDF to join against.
Also note that the UDF could take an extra parameter as to how deep "topmost" is, making this more general that it currently is in its hardcoded form.
If you modified your cte to include the depth i.e.
WITH yourcte AS
(
SELECT EmployeeId, ManagerNTID, ManagerID, NTID, FullName, 0 AS Depth
FROM Employees
WHERE NTID = #NTID
UNION ALL
SELECT e.EmployeeId, e.ManagerNTID, e.ManagerID, e.NTID, e.FullName, y.Depth + 1
FROM Employees e
JOIN yourcte y ON e.ManagerNTID = y.NTID
)
You can then order your output by depth (as the user in the input parameter should be at depth zero). Using this you should also be able to limit the depths you return and aggregate defects where depth >= 1
Edit
With the SQL I added above you basically want to rollup all defects to the user at Level 1? So, the NTID of the user at this level becomes the group by item for all records with depth >= 1. Another edit to the cte below adds the NTID as GroupingID which you can use to group by / rollup
WITH yourcte AS
(
SELECT EmployeeId, ManagerNTID, ManagerID, NTID
,FullName, 0 AS Depth, NTID as GroupingID
FROM Employees
WHERE NTID = #NTID
UNION ALL
SELECT e.EmployeeId, e.ManagerNTID, e.ManagerID, e.NTID
,e.FullName, y.Depth + 1, CASE
WHEN y.Depth + 1 = 1 THEN e.NTID
ELSE y.GroupingId
END
FROM Employees e
JOIN yourcte y ON e.ManagerNTID = y.NTID
)
here is the long dummy way of doing it. i have it working but the solution could be much better. i am hoping someone will post a SQL2005 way of getting this done...
alter PROC sel_DefectReportByManagerNTID_rollup
(#ManagerNTID NVARCHAR(100))
AS
CREATE TABLE #DefectCounts
(
id INT IDENTITY(1, 1) ,
MgrRolledInto NVARCHAR(100) NULL,
AltBusinessSeverity NVARCHAR(100) NULL,
DefectCount INT NULL
);
CREATE TABLE #directReports
(
pk INT IDENTITY(1, 1) ,
directReportNTID NVARCHAR(100) NULL
);
INSERT INTO #directReports
SELECT NTID FROM Employees WHERE ManagerNTID = #ManagerNTID
--select * from #directReports
DECLARE #maxPK INT;
SELECT #maxPK = MAX(PK) FROM #directReports
DECLARE #pk INT;
SET #pk = 1
INSERT INTO #DefectCounts (MgrRolledInto,AltBusinessSeverity,DefectCount)
SELECT #ManagerNTID, d.AltBusinessSeverity, COUNT(*)
FROM Defects d
JOIN StatusCode C ON C.CodeName = d.Status AND c.scid = 10
WHERE d.AssignedTo = #ManagerNTID
GROUP BY d.AltBusinessSeverity
WHILE #pk <= #maxPK
BEGIN
/* Get one direct report at a time to aggregate their defects under them... */
DECLARE #dirRptNTID NVARCHAR(100);
SET #dirRptNTID = (SELECT directReportNTID
FROM #directReports
WHERE PK = #pk)
INSERT INTO #DefectCounts (MgrRolledInto,AltBusinessSeverity,DefectCount)
SELECT #dirRptNTID, d.AltBusinessSeverity, COUNT(*)
FROM Defects d
JOIN StatusCode C ON C.CodeName = d.Status AND c.scid = 10
JOIN (SELECT * FROM fnGetEmployeeHierarchyByUsername(#dirRptNTID) ) emp ON emp.NTID = d.AssignedTo
WHERE d.AssignedTo IS NOT NULL
GROUP BY d.AltBusinessSeverity
SELECT #pk = #pk + 1
END
SELECT e.FullName,
isnull(Urgent,0) as Urgent,
isnull(High,0) as High,
isnull(Medium,0) as Medium,
isnull(Medium3000,0) as Medium3000,
isnull(Low,0) as Low
FROM ( select * from fnGetEmployeeHierarchyByUsername (#ManagerNTID) where depth <= 1) e
left outer join (
SELECT MgrRolledInto,
SUM([1-Urgent]) AS Urgent,
SUM([2-High]) AS High,
SUM([3-Medium]) AS Medium,
SUM([3-Medium (3000)]) AS Medium3000,
SUM([4-Low]) AS Low
FROM #DefectCounts dfs
PIVOT
(sum(DefectCount) FOR AltBusinessSeverity IN ([1-Urgent],[2-High],[3-Medium],[3-Medium (3000)],[4-Low])) V
GROUP BY MgrRolledInto
) def_data on def_data.MgrRolledInto = e.NTID
order by e.depth

passing null to CTE to get all records as a default?

I have the CTE as a UDF and am trying to get it to take a default value of nothing in which case the result returned should be everything.
I want to call it as a default like this:
select * from fnGetEmployeeHierarchyByUsername
my UDF/ CTE is:
alter FUNCTION [dbo].[fnGetEmployeeHierarchyByUsername]
(
#AMRSNTID varchar(100) = null
)
RETURNS TABLE
AS
RETURN
(
WITH yourcte AS
(
SELECT EmployeeId, ManagerAMRSNTID, ManagerID, AMRSNTID, FullName, 0 as depth--, Name
FROM Employees
WHERE AMRSNTID = #AMRSNTID
UNION ALL
SELECT e.EmployeeId, e.ManagerAMRSNTID, e.ManagerID, e.AMRSNTID, e.FullName, y.depth+1 as depth--, e.Name
FROM Employees e
JOIN yourcte y ON e.ManagerAMRSNTID = y.AMRSNTID
)
SELECT EmployeeId, ManagerID, AMRSNTID, FullName, depth--, Name
FROM yourcte
)
How can I get it to work like this?
Try this in the UDF
WHERE AMRSNTID = ISNULL(#AMRSNTID, AMRSNTID)
And call it thus
select * from fnGetEmployeeHierarchyByUsername(DEFAULT)
--or
select * from fnGetEmployeeHierarchyByUsername(NULL)
Edit: without seeing your data or how the hierarchy looks, you need to add the an OPTION clause
If it runs forever, you have a circular link somewhere
select * from fnGetEmployeeHierarchyByUsername(DEFAULT) OPTION (MAXRECURSION 0)
Note: you can't have the OPTION inside the UDF
Edit2: my mistake sorry. You need to start at employees who have no manager (i.e. start at the top)
Something like
WHERE
(#AMRSNTID IS NOT NULL AND AMRSNTID = #AMRSNTID)
OR
(#AMRSNTID IS NULL AND ManagerAMRSNTID IS NULL)
alter FUNCTION [dbo].[fnGetEmployeeHierarchyByUsername]
(
#AMRSNTID varchar(100) = null
)
RETURNS TABLE
AS
RETURN
(
WITH yourcte AS
(
SELECT EmployeeId, ManagerAMRSNTID, ManagerID, AMRSNTID, FullName, 0 as depth--, Name
FROM Employees
WHERE (AMRSNTID = #AMRSNTID) or (#AMRSNTID is null and managerID is null)
UNION ALL
SELECT e.EmployeeId, e.ManagerAMRSNTID, e.ManagerID, e.AMRSNTID, e.FullName, y.depth+1 as depth--, e.Name
FROM Employees e
JOIN yourcte y ON e.ManagerAMRSNTID = y.AMRSNTID
)
SELECT EmployeeId, ManagerID, AMRSNTID, FullName, depth--, Name
FROM yourcte
)