Question : SQL Parent/Child query

Hi all

I have a table named "Nodes" with the following columns: "companyID", "parentID"

In this table a "Parent/Child" relationship is kept, in order to know which companies belong to who.

There can be many levels while creating the compnay structure based on the data of the table.

Think of the following scenario:

The top level company id = 1 (has no parent)

1 has 3 children with the following IDs: 10, 50, 80

50 has 2 children with the following IDs: 200, 400

200 has 1 child with the following ID: 5000

So, what i need is an efficient query to return as fast as possible, all the parents and children (whole structure) of a given company ID.

For example, given the ID:200 query should return the following results (two columns: companyID AND its parentID):

column A    column B
   50           1
   400          50
   5000         200
   10           1
   80           1

Answer : SQL Parent/Child query

The code which retrieves the whole company spider from one given point is in the following SP. It supports multiple parents and cyclic ownership. The code is just the first working saample and it can be optimized for sure.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
 
-- =====================================================================
-- Author:      Pavel Celba
-- Create date: 2009-10-22
-- Description: Retrieve the whole company structure from given member
-- =====================================================================
CREATE PROCEDURE dbo.sp_GetCompanySpider 
	@CompanyID int
AS
BEGIN
	SET NOCOUNT ON;
	
	-- Return nulls for no parameters or company which does not exist
	IF @CompanyID IS NULL OR NOT EXISTS (SELECT CompanyID FROM Nodes WHERE CompanyID = @CompanyID)
	BEGIN
	  SELECT null AS CompanyID, null AS ParentID
	  RETURN
	END
	
	-- Temporary results table
	CREATE TABLE #AllParents (CompanyID int, ParentID int, IsCyclic bit, IsProcessed bit)
	
	-- Look for all parents
	INSERT INTO #AllParents
	SELECT CompanyID, ParentID, 0, 
	       CASE WHEN ParentID IS NULL THEN 1 ELSE 0 END
	  FROM Nodes
	 WHERE CompanyID = @CompanyID
	
	DECLARE @cID int, @pID int, @ccID int, @ppID int
	
    WHILE EXISTS (SELECT 1 FROM #AllParents WHERE isProcessed = 0)
    BEGIN
      
      SELECT TOP 1 @pID = ParentID, @cID = CompanyID
        FROM #AllParents 
       WHERE isProcessed = 0
      
      UPDATE #AllParents SET isProcessed = 1
       WHERE ParentID = @pID AND CompanyID = @cID
      
      DECLARE par CURSOR FOR
      SELECT CompanyID, ParentID 
	    FROM Nodes
	   WHERE CompanyID = @pID
	  
	  OPEN par
	  FETCH NEXT FROM par INTO @ccID, @ppID
	  
	  WHILE @@FETCH_STATUS = 0
	  BEGIN
	    
	    UPDATE #AllParents SET IsCyclic = 1
         WHERE CompanyID = @ccID AND (ParentID = @ppID  OR (@ppID is NULL AND ParentID is null))
	    IF @@RowCount = 0
	      INSERT INTO #AllParents 
	        VALUES (@ccID, @ppID, 0, CASE WHEN @ppID IS NULL THEN 1 ELSE 0 END)
	    
	    FETCH NEXT FROM par INTO @ccID, @ppID
	  END
	  
	  CLOSE par
	  DEALLOCATE par
      
    END
    
    -- Now we have all parents so we may look for children
	-- Mark top-most parents and cyclic parents as not processed to process them again
	UPDATE #Allparents SET IsProcessed = 0 WHERE ParentID IS NULL OR IsCyclic = 1
	
	CREATE TABLE #Rslt (CompanyID int, ParentID int)
	
    WHILE EXISTS (SELECT 1 FROM #AllParents WHERE isProcessed = 0)
    BEGIN
      
      SELECT TOP 1 @cID = CompanyID, @pID = ParentID
        FROM #AllParents 
       WHERE isProcessed = 0
      
      UPDATE #AllParents SET isProcessed = 1
       WHERE CompanyID = @cID AND (ParentID = @pID OR (@pID is NULL AND ParentID is null))
      
      DECLARE chld CURSOR FOR
      SELECT CompanyID, ParentID 
	    FROM Nodes
	   WHERE ParentID = @cID
	  
	  OPEN chld
	  FETCH NEXT FROM chld INTO @ccID, @ppID
	  
	  WHILE @@FETCH_STATUS = 0
	  BEGIN
	    
	    IF NOT EXISTS (SELECT CompanyID FROM #Rslt WHERE CompanyID = @ccID AND ParentID = @ppID)
	    BEGIN
	      INSERT INTO #Rslt VALUES (@ccID, @ppID)
	      INSERT INTO #AllParents VALUES (@ccID, @ppID, 0, 0)
	    END
	    
	    FETCH NEXT FROM chld INTO @ccID, @ppID
	  END
	  
	  CLOSE chld
	  DEALLOCATE chld
      
    END
    
	--SELECT * FROM #Allparents
	SELECT * FROM #Rslt
	
END
GO
Random Solutions  
 
programming4us programming4us