可以使用CTE更新通行证吗?

时间:2012-02-17 07:01:45

标签: sql-server-2008 recursion common-table-expression

我继承了一个具有以下结构的表:

rowID  rn   userID  Data1  Data2  Data3
-----  --   ------  -----  ----   ----
1      1    1       A      null   123
2      2    1       B      111    null
3      1    2       C      222    333
4      2    2       D      null   null
5      3    2       E      111    null
6      1    3       F      333    222

需要插入第一个recs(rn=1),而其余的(rn <>1)需要更新插入(顺序)。我可以使用where rn = 1轻松插入并检查是否缺少userID。

我的问题是我现在需要使用rn <>1按顺序更新所有rec,以便用户表反映最新状态。也就是说,UPDATE之后的用户表应如下所示:

rowID  userID  Data1  Data2  Data3
-----  ------  -----  -----  -----
1      1       B      111    123
2      2       E      111    333
3      3       F      333    222

我的想法是写一个CTE,其中每个“通行证”将抓住所有rec,其中rn = 2,然后rn = 3,然后rn = 4 ....直到我没有更多的处理。这样,我可以更新。

这可能(或者我应该使用do-while)?如果是这样,我需要一个递归的或“常规”CTE吗?

以下是我的尝试:

;with my_cte (rowID, rn, userID, Data1, Data2,  Data3, val) As 
(
    SELECT rowID, rn, userID, Data1, Data2,  Data3, val
    from @MyTempTable x
    where rn =1

        UNION ALL

    SELECT rowID, rn, userID, Data1, Data2,  Data3, b.val +1
    from @MyTempTable y
    INNER JOIN
        my_cte b
    ON  y.userID = b.userID
    WHERE y.rn = b.val +1 

)
UPDATE  userTable
SET
    [Data1] = COALESCE(c.Data1, [Data1])
    ,[Data2]= COALESCE(c.Data2, [Data2])
    ,[Data3]= COALESCE(c.Data3, [Data3])
From @MyTempTable c
JOIN
    (   SELECT user_id
        FROM my_cte
        WHERE rn<>1
    ) b
ON  b.user_id = c.user_id
WHERE 
    EXISTS
        (   Select userID 
            from userTable q
            Where q.userId = b.userId
        )

我无法使其工作,看起来只有第一行正在更新。有什么想法吗?我是CTE的菜鸟。最重要的是我想知道CTE究竟在做什么......更新是否可以在“通行证”中运行?

1 个答案:

答案 0 :(得分:1)

跟随CTE返回给定输入的给定输出。您可以将这些结果用作将记录插入另一个表的起点。

它的要点是

  • 使用递归CTE,从rn=1
  • 的所有行开始
  • 在递归部分中,如果可用,从递归部分中选择Data1-3,否则保留现有值(COALESCE)。现在CTE的结果是您的最终值+ rn=1
  • 的初始值
  • 为现有ROW_NUMBER上的每个userIDORDER DESC添加rn。这可确保最新值获得rownumber 1.
  • 最后用rownumber 1选择all并根据你的最终结果添加另一个rownumber。

SQL声明

;WITH q AS (
  SELECT  rn
          , UserID
          , Data1
          , Data2
          , Data3
  FROM    Inherited
  WHERE   rn = 1
  UNION ALL
  SELECT  i.rn
          , i.UserID
          , COALESCE(i.Data1, q.Data1)
          , COALESCE(i.Data2, q.Data2)
          , COALESCE(i.Data3, q.Data3)
  FROM    q
          INNER JOIN Inherited i ON i.rn = q.rn+1 AND i.userID = q.userID
 )
 SELECT rn = ROW_NUMBER() OVER (ORDER BY userID)
        , *
 FROM   (
           SELECT UserID
                  , Data1
                  , Data2
                  , Data3 
                  , rn = ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY rn DESC)
           FROM   q

        ) t
WHERE   rn = 1                          

测试脚本

;WITH Inherited (rowID, rn, userID, Data1, Data2, Data3) AS (
  SELECT * FROM (VALUES 
    (1, 1, 1, 'A', null, '123')
    , (2, 2, 1, 'B', '111', null)
    , (3, 1, 2, 'C', '222', '333')
    , (4, 2, 2, 'D', null, null)
    , (5, 3, 2, 'E', '111', null)
    , (6, 1, 3, 'F', '333', '222')
  ) a (b, c, d, e, f, g)
)
, q AS (
  SELECT  rn
          , UserID
          , Data1
          , Data2
          , Data3
  FROM    Inherited
  WHERE   rn = 1
  UNION ALL
  SELECT  i.rn
          , i.UserID
          , COALESCE(i.Data1, q.Data1)
          , COALESCE(i.Data2, q.Data2)
          , COALESCE(i.Data3, q.Data3)
  FROM    q
          INNER JOIN Inherited i ON i.rn = q.rn+1 AND i.userID = q.userID
 )
 SELECT rn = ROW_NUMBER() OVER (ORDER BY userID)
        , *
 FROM   (
           SELECT UserID
                  , Data1
                  , Data2
                  , Data3 
                  , rn = ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY rn DESC)
           FROM   q

        ) t
WHERE   rn = 1