Search code examples
sqlsql-servert-sqlsql-server-2008-r2treenode

Cascade functions SQL to go from tree structure to flat data structure


I have 2 tables:

table1 t_SearchCriteria:

------------------------------
ID   |  VALUE     | IDParent   |
-----|------------|------------|
 0   |   root     |     -1     |
-----|------------|------------|
 1   | JAMES      |      0     |
-----|------------|------------|
 2   | ISAC       |      0     |
-----|------------|------------|
 3   | LISA       |      1     |
-----|------------|------------|
 4   | Andrew     |      3     |
-----|------------|------------|
 5   | LISA       |      2     |
-----|------------|------------|
 6   | EZREAL     |      5     |
-----|------------|------------|
 10  | twitch     |      2     |
-----|------------|------------|
 13  | LUX        |      0     |
-----|------------|------------|
 14  | LISA       |      13    |
-----|------------|------------|
 15  | EZREAL     |      14    |
-----|------------|------------|

EDIT: here is a representation of the tree:

                                            _______root_______
                                           /        |         \
                                       JAMES       ISAC       LUX
                                         |        /   \         |
                                        LISA   TWITCH  LISA    LISA
                                         |              |       |
                                       Andrew        EZREAL   EZREAL

and my second table is like the following:

table t_Path

idPath|Person1|  Son  |grandSon|grandgrandSon|
------|-------|-------|--------|-------------|
   1  |root   |JAMES  | LISA   |ANDREW       |
------|-------|-------|--------|-------------|
   2  |root   |ISAC   | LISA   |EZREAL       |
------|-------|-------|--------|-------------|
   3  |root   |ISAC   | NULL   |TWITCH       |
------|-------|-------|--------|-------------|
   4  |root   |LUX    | NULL   |  NULL       |
------|-------|-------|--------|-------------|
   5  |root   |LUX    | LISA   |  NULL       |
------|-------|-------|--------|-------------|
   6  |root   |LUX    | LISA   | EZREAL      |
------|-------|-------|--------|-------------|

My need is to figure out a way (function or procedure) that starts from table 2 (t_Path) and find each leaf (value of grandgrandSon if not null otherwise grandson if not null etc...) id in t_searchCriteria table:

Since we can have the same value of node in the t_search criteria table then the unicity of a node is its value and its parent value and its grandParentValue (and we have another rule; a parent can't have 2 children with same name)

I have tried to make a function but I didn't find a way to do a function inside another function besides working with objects like in c# or another programming language.

I need to make a function that takes an int ID which is the ID of a path from table t_Path and figure out the leaf of the path (this is done), the problem here is how to get the id of that leaf from t_searchCriteria table since we can have multiple criteria with same value (name) even with same parent name, the grandParent Value will make difference.

for example of execution: Select FunctionGetCriteriaId(6) will return 15

where 6 is the id of the path : 6 |root |LUX | LISA | EZREAL |

and 15 is the id of the criteria : 15 | EZREAL | 14 |

Can anyone help me to figure this out please?

EDIT: to be more specific the function takes the id of the path in table 2, for example 5 ( 5 |root |LUX | LISA | NULL |) and returns the id of "LISA" (the leaf not the others ;) ) in table 1 that is 14. (of course taking note of the rules set before.)

EDIT 2: updated unicity condition in the tree


Solution

  • You can do this easily using LEFT JOINS and MAX and COALESCE. This is full working example. You can play with it:

    DECLARE @t_SearchCriteria TABLE
    (
        [ID] SMALLINT
       ,[Value] VARCHAR(12)
       ,[IDParent] SMALLINT
    );
    
    INSERT INTO @t_SearchCriteria ([ID], [Value], [IDParent])
    VALUES (0, 'root', -1)
          ,(1, 'JAMES', 0)
          ,(2, 'ISAC', 0)
          ,(3, 'LISA', 1)
          ,(4, 'Andrew', 3)
          ,(5, 'LISA', 2)
          ,(6, 'EZREAL', 5)
          ,(10, 'twitch', 2)
          ,(13, 'LUX',  0)
          ,(14, 'LISA', 13)
          ,(15, 'EZREAL', 14);
    
    DECLARE @t_Path TABLE
    (
        [idPath] SMALLINT
       ,[Person1] VARCHAR(12)
       ,[Son] VARCHAR(12)
       ,[grandSon] VARCHAR(12)
       ,[grandgrandSon] VARCHAR(12)
    );
    
    INSERT INTO @t_Path ([idPath], [Person1], [Son], [grandSon], [grandgrandSon])
    VALUES (1, 'root', 'JAMES', 'LISA', 'ANDREW')
          ,(2, 'root', 'ISAC', 'LISA', 'EZREAL')
          ,(3, 'root', 'ISAC',  'TWITCH', NULL)
          ,(4, 'root', 'LUX', NULL, NULL)
          ,(5, 'root', 'LUX', 'LISA', NULL)
          ,(6, 'root', 'LUX', 'LISA', 'EZREAL');
    
    
    -- the function input parameter
    DECLARE @idPath SMALLINT = 5;
    
    -- the function body
    
    DECLARE @Person1 VARCHAR(12)
           ,@Son VARCHAR(12)
           ,@grandSon VARCHAR(12)
           ,@grandgrandSon VARCHAR(12);
    
    SELECT @Person1 = [Person1]
          ,@Son = [Son]
          ,@grandSon = [grandSon]
          ,@grandgrandSon = [grandgrandSon]
    FROM @t_Path P
    WHERE P.[idPath] = @idPath;
    
    SELECT COALESCE(MAX(S5.[ID]), MAX(S4.[ID]), MAX(S3.[ID]), MAX(S2.[ID]), MAX(S1.[ID]))
    FROM @t_SearchCriteria S1
    LEFT JOIN @t_SearchCriteria S2
        ON S1.[ID] = S2.[IDParent]
        AND S1.[Value] = @Person1
    LEFT JOIN @t_SearchCriteria S3
        ON S2.[ID] = S3.[IDParent]
        AND S2.[Value] = @Son
        AND @Person1 IS NOT NULL
    LEFT JOIN @t_SearchCriteria S4
        ON S3.[ID] = S4.[IDParent]  
        AND S3.[Value] = @grandSon
        AND @grandgrandSon IS NOT NULL
    LEFT JOIN @t_SearchCriteria S5
        ON S4.[ID] = S5.[IDParent]
        AND S4.[Value] = @grandgrandSon
    WHERE S1.[Value] = @Person1;