Flatten Adjacency List Hierarchy To A List Of All Paths

前端 未结 4 1338
梦如初夏
梦如初夏 2020-12-13 01:14

I have a Table that stores Hierarchical information using the Adjacency List model. (uses a self referential key - example below. This Table may look familiar):

<         


        
4条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2020-12-13 02:00

    As mentioned, SQL has no clean way to implement tables with dynamically varying numbers of columns. The only two solutions I have used before are: 1. A fixed number Self-Joins, giving a fixed number of columns (AS per BobInce) 2. Generate the results as a string in a single column

    The second one sounds grotesque initially; storing IDs as string?! But when the output is formatted as XML or something, people don't seem to mind so much.

    Equally, this is of very little use if you then want to join on the results in SQL. If the result is to be supplied to an Application, it Can be very suitable. Personally, however, I prefer to do the flattening in the Application rather than SQL


    I'm stuck here on a 10 inch screen without access to SQL so I can't give the tested code, but the basic method would be to utilise recursion in some way;
    - A recursive scalar function can do this
    - MS SQL can do this using a recursive WITH statement (more efficient)

    Scalar Function (something like):

    CREATE FUNCTION getGraphWalk(@child_id INT)
    RETURNS VARCHAR(4000)
    AS
    BEGIN
    
      DECLARE @graph VARCHAR(4000)
    
      -- This step assumes each child only has one parent
      SELECT
        @graph = dbo.getGraphWalk(parent_id)
      FROM
        mapping_table
      WHERE
        category_id = @child_id
        AND parent_id IS NOT NULL
    
      IF (@graph  IS NULL)
        SET @graph = CAST(@child_id AS VARCHAR(16))
      ELSE
        SET @graph = @graph + ',' + CAST(@child_id AS VARCHAR(16))
    
      RETURN @graph
    
    END
    
    
    SELECT
      category_id                         AS [category_id],
      dbo.getGraphWalk(category_id)       AS [graph_path]
    FROM
      mapping_table
    ORDER BY
      category_id
    

    I haven't used a recursive WITH in a while, but I'll give the syntax a go even though I don't have SQL here to test anything :)

    Recursive WITH

    WITH
      result (
        category_id,
        graph_path
      )
    AS
    (
      SELECT
        category_id,
        CAST(category_id AS VARCHAR(4000))
      FROM
        mapping_table
      WHERE
        parent_id IS NULL
    
      UNION ALL
    
      SELECT
        mapping_table.category_id,
        CAST(result.graph_path + ',' + CAST(mapping_table.category_id AS VARCHAR(16)) AS VARCHAR(4000))
      FROM
        result
      INNER JOIN
        mapping_table
          ON result.category_id = mapping_table.parent_id
    )
    
    SELECT
      *
    FROM
      result
    ORDER BY
      category_id
    


    EDIT - OUTPUT for both is the same:

    1   '1'
    2   '1,2'
    3   '1,2,3'
    4   '1,2,4'
    5   '1,2,5'
    6   '1,6'
    7   '1,6,7'
    8   '1,6,7,8'
    9   '1,6,9'
    

提交回复
热议问题