【问题标题】:SQL group with Recursive CTE具有递归 CTE 的 SQL 组
【发布时间】:2017-06-28 01:16:33
【问题描述】:

我正在开发 SQL Server 2008。我相信我的问题的答案在于递归 CTE,但任何解决方案都将不胜感激。

在下面的 sam_DB.dbo.example 表中,PID 不为空,它链接回一个 ID

    ID     | PID   
    ------ | ------
    1      | NULL  
    2      | 1     
    3      | 2     
    4      | 3     
    5      | NULL  
    6      | 5     
    7      | 6     
    8      | NULL  
    9      | NULL  
    10     | 9     

我希望我的输出有一个新字段 (CID),它将从 PID 到 ID 的链接链中的每条记录标识为组的一部分,如下所示。

   ID     | PID    | CID   
   ------ | ------ | ------
   1      | NULL   | 1     
   2      | 1      | 1     
   3      | 2      | 1     
   4      | 3      | 1     
   5      | NULL   | 2     
   6      | 5      | 2     
   7      | 6      | 2     
   8      | NULL   | 3     
   9      | NULL   | 4     
   10     | 9      | 4     

【问题讨论】:

    标签: sql sql-server sql-server-2008 recursion


    【解决方案1】:

    你是正确的,你需要一个 CTE。

    您需要定义查询的第一部分以选择顶级记录(即没有父级的记录):

    select ID, PID, ID
    from @t
    where PID is null
    

    然后,对于添加到结果 CTE 的每一行(即,首先对于上述查询返回的那些记录,然后对于查询的第二部分添加的每一新行,重复每次添加,直到没有新添加已制作)您应该从源表中添加父 id 与先前添加的行的 id 匹配的所有记录。

    select t.ID, t.PID, c.CID
    from cte c
    inner join @t t
    on t.PID = c.ID
    

    除了这个逻辑之外,唯一需要注意的是,第一个表达式的 CID 列采用记录的 ID,而对于第二个表达式返回的那些记录,它采用父记录的 CID。

    完整代码

    --set up the demo data
    declare @t table (ID int not null, PID int null)
    insert @t 
    values (1, null)
    , (2,1)
    , (3,2)
    , (4,3)
    , (5,null)
    , (6,5)
    , (7,6)
    , (8,null)
    , (9,null)
    , (10,9)
    
    --actual demo
    ;with cte (ID, PID, CID) as (
    
        --select out top most (ancestor) records; setting CID to ID (since they're the oldest ancestor in their own chain, given they don't have parents)
        select ID, PID, ID
        from @t
        where PID is null
    
        union all
    
        --select each record that is a child of the record we previously selected, holding the ancestor as the parent record's ancestor
        select t.ID, t.PID, c.CID
        from cte c
        inner join @t t
        on t.PID = c.ID
    )
    select * 
    from CTE 
    order by ID
    

    【讨论】:

      【解决方案2】:

      你必须使用通用文本表达式Row_Number窗口函数

      CREATE TABLE #TblTemp(ID int,PID int)
      
      
      INSERT INTO #TblTemp(ID ,PID ) VALUES (1,NULL),(2,1),(3,1),(4,3),(5,NULL),(6,5),(7,6),(8,NULL),(9,NULL),(10,9)
      
      
      ;WITH CTE (ID, PID, CID) AS (
      
          SELECT ID, PID, ROW_NUMBER() OVER(ORDER BY ID) RN
          FROM #TBLTEMP
          WHERE PID IS NULL
      
          UNION ALL
      
          SELECT T.ID, T.PID, C.CID 
          FROM CTE C
          INNER JOIN #TBLTEMP T
          ON T.PID = C.ID
      )
      SELECT * 
      FROM CTE 
      ORDER BY ID
      

      【讨论】:

        【解决方案3】:

        我会发布一些简单的例子 -- 展示如何为错误链接或损坏的父/子组创建递归分组

        声明@t 表(项 varchar(2),tr int null,rc int null) insert @t select 'a',1,9 -- 没有链接 'a' - 是组父级 insert @t select 'b',2,1 -- 指向'a'的链接 insert @t select 'c',3,2 -- 链接到 'b' insert @t select 'd',4,3 -- 指向 'd' 的链接 insert @t select 'e',6,7 -- 没有链接 'e' - 是不同的组 insert @t select 'f',8,2 -- 链接到'c'

        -- 基于父项名称的 grn-group 名称; -- 基于父项 id 的 gid-group 名称; -- tr-transactionID ; rc-recursiveID; -- rc_New-new recursiveID 使用; rc_Old - 原始递归ID

        ;以 cte 为 ( 选择 grn=s.item, gid=s.tr, s.item, s.tr, rc_New=t.tr, rc_Old=s.rc from @t s left join @t t on t.tr=s.rc where (t.tr is NULL or s.rc is NULL) 联合所有 选择 c.grn, c.gid,s.item, s.tr, rc_New=s.rc, rc_Old=s.rc from cte c join @t s on s.rc=c.tr 其中 s.rc 不为 NULL ) select * from cte order by 2,3

        选项(MAXRECURSION 32767,FAST 100)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-11-03
          • 1970-01-01
          • 2013-11-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多