SQL递归并枢纽创建层次结构

时间:2019-09-20 17:19:02

标签: sql sql-server

我必须创建层次结构并将其应用于客户,而我无法控制客户表或其数据库。

为此,我正在创建一个单独的数据库,其中将包含虚拟客户和映射表。

还有其他一些过程可以使用此脚本的结果,并将其从不受控制的Db应用于客户。

VirtualCustomer表-我可以控制此表的设计。

这些“客户”代表必须应用的层次结构。

VirCustID    |    ParentVirCustID    |    Name
2001         |    NULL               |    Vehicle
2002         |    2001               |    Car
2003         |    NULL               |    Tech
2004         |    2003               |    Comms

HierarchyAssignment Table -我可以控制此表的设计

使用此表作为将客户加入虚拟客户的机制。然后,虚拟客户将拥有其余的层次结构。

重要:我将CustID映射到层次结构的最低级别,因此需要从下往上递归。

CustID是该数据库之外的客户的ID,我无法控制。我也不需要加入该表。

CustID    |    VirCustID
1001      |    2001
1002      |    2001
1003      |    2003

我正在尝试获得结果:

CustID    |    L1ID    |    L1Name    |    L2ID    |    L2Name 
1001      |    2001    |    Vehicle   |    2002    |    Car
1002      |    2001    |    Vehicle   |    2002    |    Car
1003      |    2003    |    Tech      |    2004    |    Comms
1004      |    NULL    |    NULL      |    NULL    |    NULL

目前我只对2个级别感兴趣,尽管有可能需要增加它,因此使用了一个映射表。我需要能够支持L n ID和名称。

我认为我需要CTE递归并且可能需要重做,并且尝试了一些我从其他Q / A中获得的东西,尽管还没有成功。

这是我正在玩的脚本之一。我只是不知道从哪里开始,将不胜感激地收到任何指针。

WITH cp AS 
(
    SELECT  p.VirCustID, p.parentVirCustID, 0 as [level]
    FROM VirtualCustomer as p
    WHERE p.parentVirCustIDis null

    UNION ALL

    SELECT p2.VirCustID, p2.parentVirtualId, c.[level]+1
    FROM  VirtualCustomer as p2
    JOIN cp as c on p2.parentVirCustID= c.VirtCustID
)
select * from cp

3 个答案:

答案 0 :(得分:0)

您可以通过执行简单的连接(假设您在执行此代码的任何地方都可以访问所有相关表),在单个查询中完成所需的结果。

这是具有所需输出的数据库小提琴: https://www.db-fiddle.com/f/hsdHCRJNcsg7ZpXbgJ7mbS/0

层次结构链接有点违反直觉,因为您是将父级“反向”链接到子级。

以下是查询:

SELECT    cust.custid
         ,sq_levelone.vircustid AS l1id
         ,sq_levelone.name      AS l1name
         ,sq_leveltwo.vircustid AS l2id
         ,sq_leveltwo.name      AS l2name
  FROM    sq_sourcesystemcustids  cust
          LEFT JOIN sq_hierarchyassignment hier ON cust.custid = hier.custid
          LEFT JOIN sq_virtualcustomer sq_levelone ON (hier.vircustid = sq_levelone.vircustid)
          LEFT JOIN sq_virtualcustomer sq_leveltwo ON (sq_levelone.vircustid = sq_leveltwo.parentvircustid)

答案 1 :(得分:0)

扩展Brent的答案,因为您希望在单个查询中执行此操作,所以您可能最终会使用他的方法,但将其推断出所需的级别。

SELECT DISTINCT c.custid, 
            Levelone.vircustid   AS l1id, 
            Levelone.NAME        AS l1name, 
            Leveltwo.vircustid   AS l2id, 
            Leveltwo.NAME        AS l2name, 
            Levelthree.vircustid AS l3id, 
            Levelthree.NAME      AS l3name, 
            Levelfour.vircustid  AS l4id, 
            Levelfour.NAME       AS l4name, 
            Levelfive.vircustid  AS l5id, 
            Levelfive.NAME       AS l5name, 
            Levelsix.vircustid   AS l6id, 
            Levelsix.NAME        AS l6name, 
            Levelseven.vircustid AS l7id, 
            Levelseven.NAME      AS l7name, 
            Leveleight.vircustid AS l8id, 
            Leveleight.NAME      AS l8name, 
            Levelnine.vircustid  AS l9id, 
            Levelnine.NAME       AS l9name, 
            Levelten.vircustid   AS l10id, 
            Levelten.NAME        AS l10name 
FROM   sourcecustomerids c 
   LEFT JOIN hierarchyassignment h 
          ON c.custid = h.custid 
   LEFT JOIN virtualcustomer Levelone 
          ON ( h.vircustid = Levelone.vircustid ) 
   LEFT JOIN virtualcustomer Leveltwo 
          ON ( Levelone.vircustid = Leveltwo.parentvircustid ) 
   LEFT JOIN virtualcustomer Levelthree 
          ON ( Leveltwo.vircustid = Levelthree.parentvircustid ) 
   LEFT JOIN virtualcustomer Levelfour 
          ON ( Levelthree.vircustid = Levelfour.parentvircustid ) 
   LEFT JOIN virtualcustomer Levelfive 
          ON ( Levelfour.vircustid = Levelfive.parentvircustid ) 
   LEFT JOIN virtualcustomer Levelsix 
          ON ( Levelfive.vircustid = Levelsix.parentvircustid ) 
   LEFT JOIN virtualcustomer Levelseven 
          ON ( Levelsix.vircustid = Levelseven.parentvircustid ) 
   LEFT JOIN virtualcustomer Leveleight 
          ON ( Levelseven.vircustid = Leveleight.parentvircustid ) 
   LEFT JOIN virtualcustomer Levelnine 
          ON ( Leveleight.vircustid = Levelnine.parentvircustid ) 
   LEFT JOIN virtualcustomer Levelten 
          ON ( Levelnine.vircustid = Levelten.parentvircustid ) 

如果您需要添加或删除额外的图层,如何将它们串联在一起应该很明显。如果使用键盘进行生活,D / p大约需要5-10秒才能添加/删除

如果您愿意使用存储过程方法,则可以根据数据动态创建正确数量的级别,但是我认为在单个查询中不容易做到这一点。如果您对程序选项感兴趣,我可能会稍作尝试。

HTH

答案 2 :(得分:0)

我正在提交一个替代答案,因为将原始答案保留在网上仍然很有用(如果您选择按照Combee的说明进行操作)。此答案同时使用了递归(您的初始查询,已修改)和数据透视。

以下代码将允许您按列构建层次结构,但有一些注意事项。 如果希望扩展层数,则仍然需要更改代码。这样做的原因是SQL Server需要列定义(如果我们不编写动态SQL的话)。此外,名称不会在结果中传播。您可以使用函数或将其作为select语句的一部分来获取它们。

要将新级别添加到层次结构中(已包含10个),您将添加一个附加列(即sq_consolidated数组和sq_merge数组中的[11]或[n],将max([n])作为LnID添加到sq_final。

此答案还取决于您在层次结构的最低级别上进行映射(这是要求的一部分)。

WITH
    sq_hierarchylevels AS
        (SELECT    vircustid
                  ,parentvircustid
                  ,name
                  ,1      AS hierlevel
                  ,[name] AS constname
           FROM    virtualcustomer
          WHERE    parentvircustid IS NULL
         UNION ALL
         SELECT    i.vircustid
                  ,i.parentvircustid
                  ,i.name
                  ,hierlevel + 1
                  ,hier.constname
           FROM    virtualcustomer  i
                   INNER JOIN sq_hierarchylevels hier ON hier.vircustid = i.parentvircustid),
    sq_consolidated AS
        (SELECT    *
           FROM    sq_hierarchylevels hier
                   PIVOT (max( vircustid )
                         FOR hierlevel
                         IN( [1],[2],[3],[4],[5],[6],[7],[8],[9],[10] )) AS piv
          WHERE    parentvircustid IS NOT NULL),
    sq_merge AS
        (SELECT    orig.vircustid
                  ,orig.parentvircustid
                  ,orig.[name]
                  ,vircustid   AS l1id
                  ,orig.[name] AS l1name
                  ,[2],[3],[4],[5],[6],[7],[8],[9],[10]
           FROM    virtualcustomer  orig
                   LEFT JOIN sq_consolidated fills ON (orig.name = fills.constname)
          WHERE    orig.parentvircustid IS NULL),
    sq_final AS
        (SELECT      vircustid
                    ,[name]
                    ,max( l1id ) AS l1id
                    ,max( [2] ) AS l2id
                    ,max( [3] ) AS l3id
                    ,max( [4] ) AS l4id
                    ,max( [5] ) AS l5id
                    ,max( [6] ) AS l6id
                    ,max( [7] ) AS l7id
                    ,max( [8] ) AS l8id
                    ,max( [9] ) AS l9id
                    ,max( [10] ) AS l10id
             FROM    sq_merge
         GROUP BY    vircustid, name)
SELECT      *
    FROM    sq_final
ORDER BY    vircustid