Как обновить родительское поле на SQL сервере - PullRequest
0 голосов
/ 03 августа 2020

Мои данные выглядят так

ID Text IsParent ParentID
-------------------------
1  A    1        NULL
2  B    0        NULL
3  C    0        NULL
4  D    0        NULL
5  E    1        NULL
6  F    0        NULL
7  G    1        NULL
8  H    0        NULL

Я хочу заполнить ParentID предыдущим parentID.

Данные упорядочены так:

ID : 2,3,4 have parentID : 1
ID : 6 has parentID : 5
ID : 8 has parentID : 7

Как это сделать с помощью SQL?

Я пробовал использовать курсор, но он слишком медленный.

Вот мой код:

DECLARE cur1 CURSOR FOR
    SELECT ID Text IsParent ParentID 
    FROM x2 
    ORDER BY ID

OPEN cur1

FETCH NEXT FROM cur1 INTO @ID, @Text, @IsParent, @ParentID

WHILE @@FETCH_STATUS = 0
BEGIN
    IF @IsParent = 1
    BEGIN
        SET @LastParentID = @ID
    END
    ELSE
    BEGIN
        UPDATE X2 
        SET ParentID = @LastParentID 
        WHERE ID = @ID
    END

    FETCH NEXT FROM cur1 INTO @ID, @Text, @IsParent, @ParentID
END;

CLOSE cur1;
DEALLOCATE cur1;

Ответы [ 3 ]

2 голосов
/ 03 августа 2020

Вы можете сделать это с помощью APPLY. Предпосылка состоит в том, чтобы найти родительскую запись с наивысшим идентификатором, где идентификатор ниже, чем у дочерней записи.

Пример

DECLARE @x2 TABLE (ID INT NOT NULL, Text CHAR(1), IsParent BIT, ParentID INT);
INSERT @x2 (ID, Text, IsParent)
VALUES
    (1, 'A', 1), (2, 'B', 0), (3, 'C', 0), (4, 'D', 0),
    (5, 'E', 1), (6, 'F', 0), (7, 'G', 1), (8, 'H', 0);

UPDATE  c
SET     ParentID = p.ID
FROM    @x2 AS c
        CROSS APPLY
        (   SELECT  TOP 1 ID 
            FROM    @x2 AS p
            WHERE   p.IsParent = 1  -- Is a parent record
            AND     p.ID < c.ID     -- ID is lower than child record
            ORDER BY p.ID DESC      -- Order descending to get the highest ID
        ) AS p
WHERE   c.IsParent = 0
AND     c.ParentID IS NULL;

SELECT  *
FROM    @x2;

OUTPUT

ID  Text    IsParent    ParentID
---------------------------------
1   A       1           NULL
2   B       0           1
3   C       0           1
4   D       0           1
5   E       1           NULL
6   F       0           5
7   G       1           NULL
8   H       0           7
1 голос
/ 03 августа 2020

Самый быстрый способ - вставить дочерние записи с родительским идентификатором. Вместо того, чтобы заполнять родительские идентификаторы постфактум. Из кода вы сначала вставляете родительские записи и получаете вновь созданные родительские идентификаторы. Затем вставьте дочерние записи с этими недавно сгенерированными родительскими идентификаторами.

Попытка сохранить скорость запроса, подобная предложенным, со временем только усугубится по мере роста данных. То, что вы можете, не означает, что вы должны.

Также в качестве примечания, если вы планируете иметь дочерние записи дочерних записей с неизвестной глубиной. Чтобы избежать рекурсии, я бы рекомендовал изучить столбец с типом данных hierarchyid.

1 голос
/ 03 августа 2020

Для этого вы можете использовать CTE и оконную функцию.

Сначала мы создаем непрерывный идентификатор (cid), используя sum, а затем выбираем минимум ID, используя cid, созданный в первый шаг, а затем, наконец, обновление таблицы, где IsParent равно 0.

попробуйте следующее:

;WITH cte AS
(
    SELECT *, sum(t.IsParent) OVER (ORDER BY id) cid
    FROM @t t
),
cte2 AS
(
    SELECT *, min(id) OVER (PARTITION BY cid ORDER BY id) pid
    FROM cte c
)
UPDATE t
SET
    t.ParentID = pid
FROM @t t
JOIN cte2 c  ON c.id = t.ID
WHERE c.IsParent = 0

db <> fiddle demo .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...