Nested Select statement? - sql

Hi I'm very new to sql but have been passed a job in which I need to query the db(MS SQL 2005) I need to return all workers where a HeadID is given.(tables below)
So I need to get all the managers that match the HeadID and then all the workers that match those managers by ManagerID. How would I do this? Any help or any sql terminology that would help me better search for the solution would be much appreciated.
Thanks
tb_Head:
HeadID
tb_Manager:
ManagerID,
HeadID,
tb_Worker:
WorkerID,
ManagerID,

A simple way would be to do something like this:
select * from tb_Worker
join tb_Manager on tb_Worker.ManagerID = tb_Manager.ManagerID
join tb_Head on tb_Manager.HeadID = Head.HeadID
where tb_Head.HeadID = <given value>
Tune your table names and select columns as appropriate.

Use common table expression
USE AdventureWorks;
GO
WITH DirectReports(ManagerID, EmployeeID, EmployeeLevel) AS
(
SELECT ManagerID, EmployeeID, 0 AS EmployeeLevel
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT e.ManagerID, e.EmployeeID, EmployeeLevel + 1
FROM HumanResources.Employee e
INNER JOIN DirectReports d
ON e.ManagerID = d.EmployeeID
)
SELECT ManagerID, EmployeeID, EmployeeLevel
FROM DirectReports ;
GO

Sounds like you want to use a recursive CTE. The books online article talks about your kind of scenario. Here's a sample set of code that I just used in a different stackoverflow article...
CREATE TABLE dbo.ctetest (employeeid int primary key not null, managerid int null);
INSERT INTO dbo.ctetest (employeeid, managerid) SELECT 1, NULL;
INSERT INTO dbo.ctetest (employeeid, managerid) SELECT 2, 1;
INSERT INTO dbo.ctetest (employeeid, managerid) SELECT 3, 1;
INSERT INTO dbo.ctetest (employeeid, managerid) SELECT 4, 2;
INSERT INTO dbo.ctetest (employeeid, managerid) SELECT 5, 2;
INSERT INTO dbo.ctetest (employeeid, managerid) SELECT 6, 3;
INSERT INTO dbo.ctetest (employeeid, managerid) SELECT 7, 2;
INSERT INTO dbo.ctetest (employeeid, managerid) SELECT 8, 5;
INSERT INTO dbo.ctetest (employeeid, managerid) SELECT 9, 4;
INSERT INTO dbo.ctetest (employeeid, managerid) SELECT 10, 6;
INSERT INTO dbo.ctetest (employeeid, managerid) SELECT 11, 6;
WITH recursivecte (employeeid, managerid, level)
AS
(SELECT employeeid
, managerid
, 'level' = 0
FROM dbo.ctetest
WHERE managerid IS NULL
UNION ALL
SELECT ct.employeeid
, ct.managerid
, 'level' = rc.level + 1
FROM dbo.ctetest ct
JOIN recursivecte rc
ON ct.managerid = rc.employeeid)
SELECT *
FROM recursivecte rc
This should give you the hierarchy of each employee from level to level. If you want to return information about the next highest level such as a manager name, then you just need to add rc.managername to the second part of the UNION ALL, add columns to the CTE table (that's the WITH recursivecte (employeeid, managerid, level) section, and give place-holders in the first portion of the statement.

Related

How to count records in SQL

Edit: Schema taken/extrapolated from comment below
create table #employees
(
Emp_ID int,
Name varchar(50),
Dept_ID int,
);
create table #departments
(
Dept_ID int,
Dept_Name varchar(50)
);
How do I count the number of employees from table employees that work in each department in table departments and include all departments that have no employees working in them.
Welcome to SO.
This problem is quite simple to solve. The steps would be as follows:
Join the Departments table to the Employees table on the Dept_ID column.
SELECT the Dept_ID and Count() and GROUP BY the Dept_ID field.
In order to return Departments without employees you need to LEFT JOIN this aggregation to the Departments table on the Dept_ID column.
In order to return the value of 0 for departments without employees, use the ISNULL() function.
Please see the below sample script using your schema. Note that this script is written in T-SQL, as you did not mention your server type.
create table #employees
(
Emp_ID int,
Name varchar(50),
Dept_ID int,
);
create table #departments
(
Dept_ID int,
Dept_Name varchar(50)
);
insert into #employees
select 1, 'Pim', 1
union all
select 2, 'Salma', 2;
insert into #departments
select 1, 'IT'
union all
select 2, 'Marketing'
union all
select 3, 'Design'
select
d1.Dept_Name
,isnull(d2.EmployeeCount, 0) as EmployeeCount
from
#departments d1
left join
(
select
d.Dept_ID
,count(e.Dept_ID) as EmployeeCount
from
#departments d
join
#employees e
on e.Dept_ID = d.Dept_ID
group by
d.Dept_ID
)
d2
on d2.Dept_ID = d1.Dept_ID
drop table #employees
drop table #departments
As you have supplied no data please try below and see if this works for you
Create the tables, I don't know if this is similar to your table structure
CREATE TABLE tbl_EMPLOYEES (Empl_Name nvarchar(20), Dept nvarchar(15))
CREATE TABLE tbl_DEPARTMENT (Dept nvarchar(15))
Populate these tables
INSERT INTO tbl_EMPLOYEES Values ('James', 'Finance')
INSERT INTO tbl_EMPLOYEES Values ('Tim', 'HR')
INSERT INTO tbl_EMPLOYEES Values ('Sally', 'Finance')
INSERT INTO tbl_EMPLOYEES Values ('Bob', 'Sales')
INSERT INTO tbl_EMPLOYEES Values ('Sam', 'HR')
INSERT INTO tbl_EMPLOYEES Values ('James', 'Finance')
INSERT INTO tbl_DEPARTMENT Values ('Finance')
INSERT INTO tbl_DEPARTMENT Values ('HR')
INSERT INTO tbl_DEPARTMENT Values ('Sales')
INSERT INTO tbl_DEPARTMENT Values ('IT')
This query will give you the number of people in each department
SELECT Dept, Count(Dept) AS Count
FROM
(
SELECT Dept
FROM tbl_EMPLOYEES
) AS Blah_Blah
GROUP BY Dept

Self Join in SQL

I have an Employee table with few records in it, from which I want to get all the employees who work in the department where employee "scott" works and those with department number as 20.
can you give a try on the following query and let me know if this worked for you
select * from employee where deptno in (select deptno from employee where name ='scott')
Not very Descriptive, But u may try this as per the Information u've provided,
Create Table #Employees
(Id int,
EmpName nvarchar(20),
Deptno int
)
Insert into #Employees Values(1,'Henry',10)
Insert into #Employees Values(2,'Mark',20)
Insert into #Employees Values(3,'Scott',20)
Insert into #Employees Values(4,'David',10)
Insert into #Employees Values(5,'Peter',30)
Insert into #Employees Values(6,'Mary',20)
Insert into #Employees Values(7,'John',30)
Select EmpName From #Employees
Where Deptno In(Select Deptno from #Employees Where EmpName = 'Scott')
And for Complete information of Employees,
Select * From #Employees
Where Deptno In(Select Deptno from #Employees Where EmpName = 'Scott')
For example the following query returns employee names and their manager names for whom they are working. Copy & Paste the following sql, I think it may help the purpose.
Create table Emp
(
empid int primary key,
name varchar(50),
mgrid int
)
Insert into Emp(empid,name,mgrid)
values (1001,'Manish Agrahari',1001);
Insert into Emp(empid,name,mgrid)values (1002,'Deepti',1001);
Insert into Emp(empid,name,mgrid)values (1003,'Amit',1001);
Insert into Emp(empid,name,mgrid)values (1004,'Sandy',1002);
Insert into Emp(empid,name,mgrid)values (1005,'Ankit',1003);
Insert into Emp(empid,name,mgrid)values (1006,'Kapil',1002);
Run The following SQL and see the result:
SELECT e.empid, e.name, m.name "Manager" FROM Emp e, Emp m WHERE e.mgrid=m.empid;

CTE Recursive Queries

I have a table with records of employees that shows a relationship of employees and who they report to:
From_ID position TO_ID position
----------------------------------------
1 Lowest_employee 3 employee
3 employee 4 employee
4 employee 5 BOSS
2 Lowest_employee 6 employee
6 employee 3 employee
10 Lowest_employee 50 BOSS2
I would like to show results that look like this, with the employee / boss IDs:
EmployeeID BossID
--------------------
1 5
2 5
10 50
This means employees 1 and 2 report to ID 5 and employee 10 reports to another boss with ID 50.
I know I need to use CTE and Recursive Queries, but cannot understand how it can be done, I'm newer to CTE Recursive Queries.
I read this article but it doesn't make any sense to me MS link
Any help with the query required to achieve this would be useful.
This includes setting up test data, however I think this is what you want:
Test Data:
DECLARE #Table TABLE
(
From_ID int,
TO_ID int
)
INSERT INTO #Table VALUES(1,3)
INSERT INTO #Table VALUES(3,4)
INSERT INTO #Table VALUES(4,5)
INSERT INTO #Table VALUES(2,6)
INSERT INTO #Table VALUES(6,3)
INSERT INTO #Table VALUES(10,50)
Query to get answer:
;WITH Hierarchy (Employee, Superior, QueryLevel)
AS
(
--root is all employees that have no subordinates
SELECT E.From_ID, E.TO_ID, 1
FROM #Table E
LEFT
JOIN #Table S
ON S.TO_ID = E.From_ID
WHERE S.TO_ID IS NULL
--recurse up tree to final superior
UNION ALL
SELECT H.Employee, S.TO_ID, H.QueryLevel + 1
FROM Hierarchy H
JOIN #Table S
ON S.From_ID = H.Superior
)
SELECT Employee, Superior
FROM
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY Employee ORDER BY QueryLevel DESC) AS RowNumber
FROM Hierarchy
) H
WHERE RowNumber = 1
Essentially, this works by :
1) get all employees with no reportees (the root)
2) recurses up through the bosses, recording the 'level'
3) use over/partition to select only the 'final' boss
WITH q (employee, boss) AS
(
SELECT fromId, toId
FROM mytable
WHERE fromId NOT IN
(
SELECT toId
FROM mytable
)
UNION ALL
SELECT employee, toId
FROM q
JOIN mytable t
ON t.fromId = boss
)
SELECT *
FROM q
WHERE boss NOT IN
(
SELECT fromId
FROM mytable
)
You could try something like this?
DECLARE #Employees TABLE (
EmployeeId INT,
PositionName VARCHAR(50),
ReportsToId INT);
INSERT INTO #Employees VALUES (1, 'Driver', 3);
INSERT INTO #Employees VALUES (3, 'Head of Driving Pool', 4);
INSERT INTO #Employees VALUES (4, 'Corporate Flunky', 5);
INSERT INTO #Employees VALUES (2, 'Window Cleaner', 6);
INSERT INTO #Employees VALUES (6, 'Head of Office Services', 3);
INSERT INTO #Employees VALUES (10, 'Minion', 50);
INSERT INTO #Employees VALUES (5, 'BOSS', NULL);
INSERT INTO #Employees VALUES (50, 'BOSS2', NULL);
WITH Employees AS (
SELECT
EmployeeId,
1 AS [Level],
EmployeeID AS [Path],
ISNULL(ReportsToId, EmployeeId) AS ReportsToId
FROM
#Employees
WHERE
ReportsToId IS NULL
UNION ALL
SELECT
e.EmployeeID,
x.[Level] + 1 AS [Level],
x.[Path] + e.EmployeeID AS [Path],
x.ReportsToId
FROM
#Employees e
INNER JOIN Employees x ON x.EmployeeID = e.ReportsToId)
SELECT
ec.EmployeeId,
e.PositionName,
ec.[Level],
CASE WHEN ec.ReportsToId = ec.EmployeeId THEN NULL ELSE ec.ReportsToId END AS ReportsToId --Can't really report to yourself
FROM
Employees ec
INNER JOIN #Employees e ON e.EmployeeId = ec.EmployeeId
ORDER BY
ec.[Path];

Generating HierarchyID

I would like to insert the hierarchyId like this
/ - CEO (Root)
/1/ - Purchase Manager
/1/1/ - Purchase Executive
/2/ - Sales Manager
/2/1/ - Sales Executive
This is what the hierarchy i would like to use, is it right one, if so how can i do this, can any one give me some code snippet.
I came across this question while searching for information on the hierarchyid data type, and thought it would be interesting for anyone else coming after me to also see code to insert hierarchyids as per the question.
I do not claim that these are the only ways to insert hierarchyids, but hopefully it will help those who, like me, have no previous experience working with this data type.
Using this table,
create table OrgChart
(
Position hierarchyid,
Title nvarchar(50)
)
you can use Parse to directly insert the hierarchyids using the string paths:
insert into OrgChart(Position, Title)
values (hierarchyid::Parse('/'), 'CEO'),
(hierarchyid::Parse('/1/'), 'Purchase Manager'),
(hierarchyid::Parse('/1/1/'), 'Purchase Executive'),
(hierarchyid::Parse('/2/'), 'Sales Manager'),
(hierarchyid::Parse('/2/1/'), 'Sales Executive')
and use the following query to check the table
select Position.ToString(), * from OrgChart
You can also use the hierarchyid data type methods GetRoot and GetDescendant to build the hierarchy. I found this method to be more cumbersome, but I suppose using these methods is necessary if you are programmatically managing the hierarchy.
declare #root hierarchyid,
#id hierarchyid
set #root = hierarchyid::GetRoot()
insert into OrgChart(Position, Title) values (#root, 'CEO')
set #id = #root.GetDescendant(null, null)
insert into OrgChart(Position, Title) values (#id, 'Purchase Manager')
set #id = #root.GetDescendant(#id, null)
insert into OrgChart(Position, Title) values (#id, 'Sales Manager')
select #id = Position.GetDescendant(null, null) from OrgChart where Title = 'Purchase Manager'
insert into OrgChart(Position, Title) values (#id, 'Purchase Executive')
select #id = Position.GetDescendant(null, null) from OrgChart where Title = 'Sales Manager'
insert into OrgChart(Position, Title) values (#id, 'Sales Executive')
Definitely check out the links provided in the other answer, but hopefully having this code to try out will help as well.
Suppose that you have a table schema with a self-join (as shown below) and that the ManagerID of your CEO is NULL.
CREATE TABLE Employee
(
EmployeeID int NOT NULL IDENTITY(1,1) PRIMARY KEY
, JobTitle nvarchar(50) NOT NULL
, FirstName nvarchar(50) NOT NULL
, LastName nvarchar(50)
, ManagerID int
)
ALTER TABLE dbo.Employee ADD CONSTRAINT
FK_Employee_Employee FOREIGN KEY
(
ManagerID
) REFERENCES dbo.Employee
(
EmployeeID
) ON UPDATE NO ACTION
ON DELETE NO ACTION
GO
INSERT INTO Employee(JobTitle, FirstName, LastName, ManagerID)
Values ('Executive', 'Supreme', 'Leader', NULL)
INSERT INTO Employee(JobTitle, FirstName, LastName, ManagerID)
Values ('Manger', 'Boss', 'Man', 1)
INSERT INTO Employee(JobTitle, FirstName, LastName, ManagerID)
Values ('Minion', 'Bob', 'Minion', 2)
INSERT INTO Employee(JobTitle, FirstName, LastName, ManagerID)
Values ('Minion', 'Joe', 'Minion', 2)
GO
You can auto-generate an initial set of hierarchyid values using the following recursive CTE:
;WITH EmployeeHierarchy (
EmployeeHierarchyID
, EmployeeID
, JobTitle
, LastName
, FirstName
, ManagerID
)
AS (
SELECT HIERARCHYID::GetRoot() AS EmployeeHierarchyID
, EmployeeID
, JobTitle
, LastName
, FirstName
, ManagerID
FROM Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT HIERARCHYID::Parse(Manager.EmployeeHierarchyID.ToString() + (
CONVERT(VARCHAR(20), ROW_NUMBER() OVER (
ORDER BY DirectReport.EmployeeID
))
) + '/') AS EmployeeHierarchy
, DirectReport.EmployeeID
, DirectReport.JobTitle
, DirectReport.LastName
, DirectReport.FirstName
, DirectReport.ManagerID
FROM EmployeeHierarchy AS Manager
INNER JOIN Employee AS DirectReport
ON Manager.EmployeeID = DirectReport.ManagerID
)
SELECT EmployeeHierarchyID
, EmployeeID
, JobTitle
, LastName
, FirstName
, ManagerID
INTO #EmployeeHierarchy
FROM EmployeeHierarchy
ORDER BY EmployeeHierarchyID
GO
It then becomes a fairly trivial matter to add a hierarchyid column to the table, add an index on it, and then populate it by joining to the temp table.
ALTER TABLE dbo.Employee ADD
EmployeeHierarchyID hierarchyid NULL
GO
UPDATE Employee
SET Employee.EmployeeHierarchyID = #EmployeeHierarchy.EmployeeHierarchyID
FROM Employee INNER JOIN
#EmployeeHierarchy ON Employee.EmployeeID = #EmployeeHierarchy.EmployeeID
GO
SELECT EmployeeHierarchyID.ToString() AS EmployeeHierarchyIDString, EmployeeID, JobTitle, FirstName, LastName, ManagerID, EmployeeHierarchyID
FROM Employee
GO
However, bear in mind that if you want the hierarchyid data to remain consistent after you add it, there are very specific ways in which it should be maintained.

Updating records with their subordinates via CTE or subquery

Let's say I have a table with the following columns:
Employees Table
employeeID int
employeeName varchar(50)
managerID int
totalOrganization int
managerID is referential to employeeID. totalOrganization is currently 0 for all records.
I'd like to update totalOrganization on each row to the total number of employees under them.
So with the following records:
employeeID employeeName managerID totalOrganization
1 John Cruz NULL 0
2 Mark Russell 1 0
3 Alice Johnson 1 0
4 Juan Valdez 3 0
The query should update the totalOrganizations to:
employeeID employeeName managerID totalOrganization
1 John Cruz NULL 3
2 Mark Russell 1 0
3 Alice Johnson 1 1
4 Juan Valdez 3 0
I know I can get somewhat of an org. chart using the following CTE:
WITH OrgChart (employeeID, employeeName,managerID,level)
AS (
SELECT employeeID,employeeName,0 as managerID,0 AS Level
FROM Employees
WHERE managerID IS NULL
UNION ALL
SELECT Employees.employeeID,Employees.employeeName,Employees.managerID,Level + 1
FROM Employees INNER JOIN
OrgChart ON Employees.managerID = OrgChart.employeeID
)
SELECT employeeID,employeeName,managerID, level
FROM OrgChart;
Is there any way to update the Employees table using a stored procedure rather than building some routine outside of SQL to parse through the data?
After a few hours of experimentation I came up with the following. It gives the desired results. Anyone see a way to improve it?
CREATE TABLE #totalOrganization (employeeID int,managerID int,level int);
CREATE TABLE #countedOrganization (employeeID int,managerID int,orgCount int,level int);
WITH OrgChart (employeeID,managerID,level)
AS (
SELECT employeeID,0 as managerID,0 AS Level
FROM Emp
WHERE managerID IS NULL
UNION ALL
SELECT Emp.employeeID,Emp.managerID,Level + 1
FROM Emp
INNER JOIN OrgChart
ON Emp.managerID = OrgChart.employeeID
)
INSERT INTO
#totalOrganization
SELECT
employeeID,managerID,level
FROM
OrgChart;
DECLARE #maxLevel int
SELECT
#maxLevel = MAX(level)
FROM
#totalOrganization;
WHILE (#maxLevel > -1)
BEGIN
INSERT INTO
#countedOrganization
SELECT
upline.employeeID,upline.managerID,SUM(CONVERT(INT,CASE WHEN downline.orgCount IS NULL THEN 0 ELSE downline.orgCount END)) + CONVERT(INT,CASE WHEN COUNT(downline.employeeID) IS NULL THEN 0 ELSE COUNT(downline.employeeID) END),upline.level
FROM
#totalOrganization AS upline LEFT OUTER JOIN
#countedOrganization AS downline ON downline.managerID=upline.employeeID
WHERE
upline.level = #maxLevel
GROUP BY
upline.employeeID,upline.managerID,upline.level
SET #maxLevel = #maxLevel - 1
END
UPDATE
Emp
SET
totalOrg= CONVERT(INT,CASE WHEN orgCount IS NULL THEN 0 ELSE orgCount END)
FROM
#countedOrganization INNER JOIN
Emp ON #countedOrganization.employeeID=Emp.employeeID
You can add another CTE to determine the count of employees and then use that in an Update statement:
WITH OrgChart (employeeID, employeeName,managerID,level)
AS (
SELECT employeeID,employeeName,0 as managerID,0 AS Level
FROM Employees
WHERE managerID IS NULL
UNION ALL
SELECT Employees.employeeID,Employees.employeeName,Employees.managerID,Level + 1
FROM Employees
INNER JOIN OrgChart
ON Employees.managerID = OrgChart.employeeID
)
, SubordinateCount As
(
Select ManagerId, Count(*) As Total
From OrgChart
Group By ManagerId
)
Update Employees
Set TotalOrganization = SubordinateCount.Total
FROM SubordinateCount
Join Employees As E
On E.employeeId = SubordinateCount.ManagerId
ADDITION
The change in spec is that you want a count of all subordinate employees. The trick to that is to create a path of the employee to each of their managers. So, first here is my test data:
Insert Employees(EmployeeId, Name, ManagerId) Values(1, 'Alice', Null)
Insert Employees(EmployeeId, Name, ManagerId) Values(2, 'Bob', 1)
Insert Employees(EmployeeId, Name, ManagerId) Values(3, 'Charlie', 1)
Insert Employees(EmployeeId, Name, ManagerId) Values(4, 'Dan', 3)
Insert Employees(EmployeeId, Name, ManagerId) Values(5, 'Ellen', 3)
Insert Employees(EmployeeId, Name, ManagerId) Values(6, 'Fred', 5)
Insert Employees(EmployeeId, Name, ManagerId) Values(7, 'Gale', 6)
Insert Employees(EmployeeId, Name, ManagerId) Values(8, 'Harry', 6)
So, first we write a query that gives us a path to their manager:
With
OrgChart As
(
Select E.EmployeeId, E.Name, Null As ManagerId, 0 AS Level
, Cast( '/' + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100) ) As Path
From dbo.Employees As E
Where E.ManagerId Is Null
Union All
Select E.EmployeeID, E.Name, E.ManagerID, Level + 1
, Cast( OrgChart.Path + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100))
From dbo.Employees As E
Join OrgChart
On OrgChart.EmployeeId = E.ManagerID
)
Select *
From OrgChart
That produces:
EmployeeId Name ManagerId Level Path
1 Alice NULL 0 /1/
2 Bob 1 1 /1/2/
3 Charlie 1 1 /1/3/
4 Dan 3 2 /1/3/4/
5 Ellen 3 2 /1/3/5/
6 Fred 5 3 /1/3/5/6/
7 Gale 6 4 /1/3/5/6/7/
8 Harry 6 4 /1/3/5/6/8/
Now we simply need to count instances where the given employee exists in someone's path:
With
OrgChart As
(
Select E.EmployeeId, E.Name, Null As ManagerId, 0 AS Level
, Cast( '/' + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100) ) As Path
From dbo.Employees As E
Where E.ManagerId Is Null
Union All
Select E.EmployeeID, E.Name, E.ManagerID, Level + 1
, Cast( OrgChart.Path + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100))
From dbo.Employees As E
Join OrgChart
On OrgChart.EmployeeId = E.ManagerID
)
, OrgCounts As
(
Select O.EmployeeId, O.Name, O.ManagerId, O.Level, O.Path
, (Select Count(*)
From OrgChart As O1
Where O1.Path Like '%/' + Cast(E.EmployeeId As varchar(10)) + '/%') - 1 As SubordinateTotal
From Employees As E
Join OrgChart As O
On O.EmployeeId = E.EmployeeId
)
Select O.EmployeeId, O.Name, O.ManagerId, O.Level, O.Path, O.SubordinateTotal
From OrgCounts
I subtract one from the total to exclude the current employee. Now that we've found a query to provide the proper results, we can easily use that to do an update:
With
OrgChart As
(
Select E.EmployeeId, E.Name, Null As ManagerId, 0 AS Level
, Cast( '/' + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100) ) As Path
From dbo.Employees As E
Where E.ManagerId Is Null
Union All
Select E.EmployeeID, E.Name, E.ManagerID, Level + 1
, Cast( OrgChart.Path + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100))
From dbo.Employees As E
Join OrgChart
On OrgChart.EmployeeId = E.ManagerID
)
, OrgCounts As
(
Select O.EmployeeId, O.Name, O.ManagerId, O.Level, O.Path
, (Select Count(*)
From OrgChart As O1
Where O1.Path Like '%/' + Cast(E.EmployeeId As varchar(10)) + '/%') - 1 As SubordinateTotal
From Employees As E
Join OrgChart As O
On O.EmployeeId = E.EmployeeId
)
Update Employees
Set TotalOrganization = O.SubordinateTotal
From OrgCounts As O
Join dbo.Employees As E
On E.EmployeeId = O.EmployeeId
This can (of course) be done within a stored procedure. However, it looks very much like it cannot be done with a single (CTE) statement, as you cannot sum a given employee's subordinates + all of their subordinates (i.e. tally all descendants underneath a given item in the hierarchy), as per this error message:
GROUP BY, HAVING, or aggregate functions are not allowed in the recursive part of a recursive common table expression 'Subordinates'.
So that routine you'd write outside of SQL (start at the lowest "level" of the hierarchy, count all those employees subordinates, repeat as you iterate up the hierarchy) would have to be written within SQL.