SQL Server 2005: обновить строки в указанном порядке (например, ORDER BY)? - PullRequest
3 голосов
/ 09 июня 2010

Я хочу обновить строки таблицы в определенном порядке, как и следовало ожидать, если включить предложение ORDER BY, но SQL Server не поддерживает предложение ORDER BY в запросах UPDATE.

Я проверил этот вопрос , который дал хорошее решение, но мой запрос немного сложнее, чем указанный там.

UPDATE TableA AS Parent
SET Parent.ColA = Parent.ColA + (SELECT TOP 1 Child.ColA
    FROM TableA AS Child
    WHERE Child.ParentColB = Parent.ColB
    ORDER BY Child.Priority)
ORDER BY Parent.Depth DESC;

Итак, я надеюсь, что вы заметите, что одна таблица (TableA) содержит иерархию строк, в которой одна строка может быть родительской или дочерней для любой другой строки. Строки должны быть обновлены по порядку от самого глубокого потомка до корневого родителя. Это связано с тем, что TableA.ColA должен содержать современную конкатенацию своего текущего значения со значениями его дочерних элементов (я понимаю, что этот запрос совпадает только с одним дочерним элементом, но это ради простоты - цель пример в этом вопросе больше не требует многословия), поэтому запрос должен обновляться снизу вверх.

Решение, предложенное в вопросе, который я отметил выше, заключается в следующем:

UPDATE messages
SET status=10
WHERE ID in (SELECT TOP (10) Id
    FROM Table
    WHERE status=0
    ORDER BY priority DESC
);

Причина, по которой я не думаю, что смогу использовать это решение, заключается в том, что я ссылаюсь на значения столбцов из родительской таблицы в моем подзапросе (см. WHERE Child.ParentColB = Parent.ColB), и я не думаю, что два дочерних подзапроса будут иметь доступ к данные друг друга.

Пока что я определил только один способ объединить предложенное решение с моей текущей проблемой, и я не думаю, это работает.

UPDATE TableA AS Parent
SET Parent.ColA = Parent.ColA + (SELECT TOP 1 Child.ColA
    FROM TableA AS Child
    WHERE Child.ParentColB = Parent.ColB
    ORDER BY Child.Priority)
WHERE Parent.Id IN (SELECT Id
    FROM TableA
    ORDER BY Parent.Depth DESC);

Подзапрос WHERE..IN на самом деле не вернет подмножество строк, он просто вернет полный список идентификаторов в нужном мне порядке. Однако (я точно не знаю - скажите, пожалуйста, если я ошибаюсь) Я думаю, что предложение WHERE..IN не будет заботиться о порядке идентификаторов в скобках - оно просто проверит идентификатор строки, в которой он находится в данный момент. хочет обновить, чтобы видеть, находится ли он в этом списке (который, они все есть) в любом порядке, который он уже пытается обновить ... Это будет просто пустая трата циклов, потому что это ничего не изменит.

Итак, в заключение, я оглянулся и не могу найти способ обновления в указанном порядке (и включил причину Мне нужно обновить в этом порядке, потому что я уверен, что в противном случае я получил бы очень полезные ответы «почему?», и теперь я нажимаю на «Переполнение стека», чтобы узнать, есть ли среди вас гуру, которые знают о SQL больше, чем я (что мало что говорит) знать эффективный способ сделать это. Особенно важно, чтобы я использовал только один запрос для выполнения этого действия.

Длинный вопрос, но я хотел рассказать о своих базах и дать вам, ребята, как можно больше информации, которую можно получить. :)

Есть мысли?

Ответы [ 4 ]

2 голосов
/ 09 июня 2010

Вы не можете выполнить это в одном запросе, потому что ваши обновления коррелированы (т. Е. Уровень N зависит от значения updated уровня N + 1). Реляционные движки недовольны этим явным образом из-за проблемы Halloween . План запроса сделает все возможное, чтобы обновления происходили так, как если бы они имели два этапа: один, на котором было прочитано текущее состояние, и второй, на котором применено обновленное состояние. При необходимости они будут буферизовать промежуточные таблицы только для того, чтобы сохранить этот очевидный порядок выполнения (read all-> write all) Поскольку ваш запрос, если я правильно понимаю, пытается нарушить эту предпосылку, я не вижу, как вы добьетесь успеха.

1 голос
/ 16 июня 2010

Это решение SQL, состоящее из одной строки.Если вы когда-нибудь ослабите требование, чтобы это был один оператор обновления, вы можете учесть некоторые сложности

CREATE TABLE [TableA](
    [ID] [int] NOT NULL,
    [ParentID] [int] NULL,
    [ColA] [varchar](max) NOT NULL,
    [Priority] [varchar](50) NOT NULL,
    [Depth] [int] NOT NULL)
go

INSERT TableA
SELECT 1, NULL, 'p', 'Favorite', 0 UNION ALL
SELECT 2, 1, 'm', 'Favorite', 1 UNION ALL
SELECT 3, 1, 'o', 'Likeable', 1 UNION ALL
SELECT 4, 2, 'v', 'Favorite', 2 UNION ALL
SELECT 5, 2, 'v', 'Likeable', 2 UNION ALL
SELECT 6, 2, 'd', 'Likeable', 2 UNION ALL
SELECT 7, 6, 'c', 'Red-headed Stepchild', 3 UNION ALL
SELECT 8, 6, 's', 'Likeable', 3 UNION ALL
SELECT 9, 8, 'n', 'Favorite', 4 UNION ALL
SELECT 10, 6, 'c', 'Favorite', 3 UNION ALL
SELECT 11, 5, 'c', 'Favorite', 3 UNION ALL
SELECT 12, NULL, 'z', 'Favorite', 0 UNION ALL
SELECT 13, 3, 'e', 'Favorite', 2 UNION ALL
SELECT 14, 8, 'k', 'Likeable', 4 UNION ALL
SELECT 15,4, 'd', 'Favorite', 3

;WITH cte AS (
SELECT a.i, a.Depth, a.maxd, a.mind, a.maxc, a.di, a.ci, a.cdi, a.ID, a.y, CAST('' AS varchar(max))z
FROM(
    SELECT DISTINCT i = 1
    ,p.Depth
    ,maxd = (SELECT MAX(Depth) FROM TableA)
    ,mind = (SELECT MIN(Depth) FROM TableA)
    ,maxc = (SELECT MAX(c) FROM (SELECT COUNT(*) OVER(PARTITION BY ParentID) FROM TableA)f(c))
    ,di   = (SELECT MIN(Depth) FROM TableA)
    ,ci   = 1
    ,cdi  = (SELECT MIN(Depth) FROM TableA)
    ,p.ID
    ,CAST(p.ID AS varchar(max)) + p.ColA + SPACE(1) + CASE WHEN g IS NULL THEN '' ELSE '(' END 
                                     + ISNULL(g,'') + CASE WHEN g IS NULL THEN '' ELSE ')' END y
    FROM TableA p
    LEFT JOIN TableA c ON (c.ParentID = p.ID)
    CROSS APPLY (SELECT SPACE(1) + CAST(c2.ID AS varchar(max)) + ColA + SPACE(1) 
                 FROM TableA c2 WHERE ParentID = p.ID 
                 ORDER BY Priority 
                 FOR XML PATH(''))f(g)
    )a
UNION ALL
SELECT r.i, r.Depth, r.maxd, r.mind, r.maxc, r.di, r.ci, r.cdi, r.ID
,CASE WHEN di = cdi 
      THEN REPLACE(r.y,LEFT(r.z,CHARINDEX(SPACE(1),r.z,2)), r.z)
      ELSE r.y END [y]
,r.z
FROM(
    SELECT i = i + 1
    ,Depth
    ,[maxd]
    ,[mind]
    ,[maxc]
    ,CASE WHEN ci = maxc AND cdi = maxd
          THEN di + 1
          ELSE di
          END [di]
    ,CASE WHEN cdi = [maxd]
          THEN CASE WHEN ci + 1 > maxc
                    THEN 1
                    ELSE ci + 1
                    END
          ELSE ci
          END [ci]
    ,CASE WHEN cdi + 1 > maxd
          THEN mind
          ELSE cdi + 1
          END [cdi]
    ,id,y
    ,CAST(ISNULL((SELECT y FROM(
        SELECT p.Depth,p.ID
        ,SPACE(1) + CAST(p.ID AS varchar(max)) + p.ColA + SPACE(1) + 
        CASE WHEN g IS NULL THEN '' ELSE '(' END + ISNULL(g,'') 
      + CASE WHEN g IS NULL THEN '' ELSE ')' END y
        ,r1 = DENSE_RANK() OVER(ORDER BY p.ID) --child number
        ,r2 = ROW_NUMBER() OVER(PARTITION BY p.ID ORDER BY p.ID) --DISTINCT not allowed in recursive section
        FROM TableA p
        JOIN TableA c ON (c.ParentID = p.ID)
        CROSS APPLY (SELECT SPACE(1)+CAST(c2.ID AS varchar(max))+ColA+SPACE(1) 
                     FROM TableA c2 
                     WHERE ParentID = p.ID 
                     ORDER BY Priority 
                     FOR XML PATH(''))f(g)
        WHERE p.Depth = cdi AND cdi < di AND p.ID <> cte.ID
        )v
    WHERE r1 = ci 
    AND r2 = 1
    AND cte.y LIKE '%' + LEFT(v.y,CHARINDEX(SPACE(1),v.y,2) ) + '%'),'') AS varchar(max)) z
FROM cte
WHERE [di]<[maxd] or [ci]<[maxc] or [cdi]<[maxd]
)r
)--cte
UPDATE t
SET ColA = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE
        (y,SPACE(1),''),'1',''),'2',''),'3',''),'4',''),'5',''),'6',''),'7',''),'8',''),'9',''),'0','')
FROM cte
JOIN TableA t ON (t.ID = cte.ID)
WHERE di = (SELECT MAX(Depth) FROM TableA)
AND cdi  = (SELECT MAX(Depth) FROM TableA)
AND ci   = (SELECT MAX(c) FROM (SELECT COUNT(*) OVER(PARTITION BY ParentID) FROM TableA)f(c)) 
OPTION(maxrecursion 0)

SELECT * FROM TableA
DROP TABLE TableA
1 голос
/ 09 июня 2010

Операторы UPDATE будут выполняться как один запрос, а не как пошаговый результат.

Вам нужно либо использовать цикл while / курсор (uhhgg), либо, возможно, использовать представление выражения CTE длядобиться того, что вы пытаетесь, что дает вам возможность рекурсивного.

Посмотрите на

0 голосов
/ 10 июня 2010

JMTyler-

1 Какие данные находятся в ColA?Как это выглядит?

2 Как / должен быть изначально заполнен этот столбец?Я спрашиваю об этом, потому что вы сможете запустить обновление только один раз, поскольку значение в этом столбце будет изменено с предыдущего запуска.Любые дополнительные прогоны просто объединяют больше данных.Что заставляет меня поверить, что есть еще один ColC с исходным значением ColA (имя человека?)

3 Будет ли когда-нибудь удалена строка, оставившая детей?Если да, на что должен указывать их ParentColB?НОЛЬ?Затем их глубина устанавливается равной 0, и теперь они находятся на вершине иерархии?

Если вы можете ответить на это, я могу дать вам решение

Спасибо

...