Можно ли использовать CTE для обновления в проходах? - PullRequest
1 голос
/ 17 февраля 2012

Я унаследовал таблицу со следующей структурой:

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

Первые записи (rn=1) должны быть вставлены, а остальные (rn <>1) должны обновлять вставки (последовательно). Я могу легко вставить, используя where rn = 1 и проверяя отсутствие идентификатора пользователя.

Моя проблема в том, что мне нужно теперь обновлять все записи последовательно, используя rn <>1, чтобы таблица пользователя отображала последнее состояние. То есть пользовательская таблица после ОБНОВЛЕНИЙ должна выглядеть так:

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

Я думал написать CTE, где каждый «проход» будет захватывать все записи, где rn = 2, затем rn = 3, затем rn = 4 .... пока у меня не останется больше rn для обработки. Таким образом, я могу обновить в наборах.

Возможно ли это (или я должен использовать 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 Ответ

1 голос
/ 17 февраля 2012

После CTE возвращает заданный вывод для заданных вами входов. Вы можете использовать эти результаты в качестве отправной точки для вставки записей в другую таблицу.

Суть этого

  • Использовать рекурсив CTE, начиная со всех строк, где rn=1.
  • В рекурсивной части выберите Data1-3 из рекурсивной части, если она доступна, в противном случае сохраните существующее значение (COALESCE). Результатом CTE теперь являются ваши окончательные значения + начальные значения, где rn=1
  • Добавьте ROW_NUMBER для каждого userID, но ORDER DESC для существующего rn. Это гарантирует, что последние значения получат номер 1.
  • Наконец, выберите все с помощью rownumber 1 и добавьте еще одно 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                          
...