SQL Server Central is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
Search:  
 
 

Everybody Reports to Somebody

By Craig Hatley, 2007/06/04

Total article views: 4238 | Views in the last 30 days: 251
Everybody Reports To Somebody

Have you ever thought about the logistics of the organizational structure of a company? Do you know how many layers of management exist from the top to the bottom? I was recently forced to consider these very questions to support a new application that required managers to be able to access data associated with any subordinate employee. This could be an employee that directly reports to the manager or an employee that is several layers below the manager in the overall organizational structure.

For example, if my function received Bob's User ID, it should return the following people based on the sample organizational chart (See Figure 1) . . .

  • Bob
  • Sue
  • Todd
  • Mary
  • Sandy
  • Megan
  • Randy

 Figure 1 


The table that stores our employee data (see figure 2) includes a 'User ID' field which is the primary key for the table and a 'Manager User ID' field which contains the User ID of the manager that each employee directly reports to (everybody reports to somebody philosophy). If Bob has a User ID of '2' in our sample above, Sue, Todd, and Mary would all have a value of '2' in their 'Manager User ID' field indicating that they report directly to Bob (see figure 3).

Figure 2
 Figure 2 


Figure 3
 Figure 3 


Now for the tricky part . . . One of the objectives of our stored procedure is that it should not assume any foreknowledge of the organizational structure including the number of management layers. As I started this project I knew that my code would be required to perform some type of recursion, so I found a great article on the Microsoft web site that discussed recursion in SQL. After spending a little time reading the article, I discovered a couple of potential significant limitations in that SQL recursion is limited to 32 loops (to prevent infinite loop conditions) and most of the examples for passing data back up through the recursive loop chain used a data type that was restricted by a maximum length. Although our company doesn't currently have 32 layers of management hierarchy, I simply didn't want to settle for a 'work around' solution because I suspected it would eventually be an issue for us.

This caused me to start looking for alternatives which after much prayerful consideration yielded the following stored procedure . . .

CREATE PROCEDURE select_subordinates_by_manager
     @UserID int 
As
Set NoCount On

Declare @UserCount int
Set @UserCount = 1

Create Table #In (UserID int) 
Create Table #Out (UserID int) 
Create Table #Result (UserID int) 

Insert Into #In (UserID)      Values (@UserID)
Insert Into #Result (UserID)      Values (@UserID)

While @UserCount > 0
    Begin
        Insert Into #Out (UserID)
        Select UserID From Test..Employee 
        Where ManagerUserID In (Select UserID From #In)
        Select @UserCount = (Select Count(UserID) From #Out)
        If @UserCount > 0
            Begin
                Insert Into #Result (UserID)
                Select UserID From #Out
            End
        Delete From #In
        Insert Into #In
        Select UserID From #Out
        Delete From #Out
    End

Set NoCount Off

Select UserID From #Result
The stored procedure basically starts off by returning a list of the User IDs that report directly to the specified manager User ID. It then runs in a loop checking for User IDs that report to the list of User IDs that were returned in the previous loop until no more User IDs are found. The User IDs returned from each successive loop are added to the temporary table called #Result which is ultimately used to return a list of User ID values representing the subordinate employees.

We typically use the User ID list returned from the previous stored procedure in conjunction with another stored procedure (see below) to return a list representing the employees that report to the specified manager either directly or indirectly.

CREATE PROCEDURE select_subordinate_names_by_manager 
    @UserID int
As
Create Table #Subordinates (UserID int)
Insert Into #Subordinates (UserID)
Exec ('dbo.select_subordinates_by_manager ' + @UserID)
Select e.UserID, e.FirstName, e.LastName 
  From Test..Employee as e
  Join #Subordinates as s on s.UserID = e.UserID
  Order By e.FirstName, e.LastName


After looking at the code and the extensive use of temporary tables you may be concerned about how well this solution will perform. As a baseline, I measured the overall execution time of this technique in a test environment (see environment details below) with a single organizational structure branch that represents 50 layers of management and more than 4500 employees. Needless to say I was pleasantly surprised to discover that the overall execution time was only 64 ms.

Test Environment
  • SQL Platform : Microsoft SQL Server 2000 (Developer Edition)
  • Hardware Platform : Workstation Class PC (Single Pentium 4 3.2Ghz/3GB RAM)

I hope you found this article informative and interesting . . .

By Craig Hatley, 2007/06/04

Total article views: 4238 | Views in the last 30 days: 251
Your response
 
 
Related tags

Advanced Querying    
T-SQL    
 
Already registered?  

Free registration required

To read the rest of this article, and access thousands of other articles, we ask you to register on the site and subscribe to our newsletters.

Register

E-mail address:
Password:
Password (confirm):

  

Subscriptions

We ask you to register on the site and subscribe to our newsletters. Subscribing to our newsletters gets you:

  • ALL of our content (thousands of articles, scripts, and forum postings)
  • A daily newsletter (example)
  • A weekly news round up (example)
  • The opportunity to ask and answer questions in our forums
  • A daily Question of the Day to test and help you increase your knowledge of SQL Server.

We ask that you give the newsletter a try for a week. Over 200,000 SQL Server Professionals a day find it entertaining and useful. If not, you are welcome to unsubscribe at anytime.

Steve Jones
Editor, SQLServerCentral.com