Выполните обновление значения и используйте это значение для обновления другого значения во время того же процесса обновления

Запуск Microsoft SQL Azure (окончательная первоначальная версия) — 12.0.2000.8, 19 июня 2024 г., 16:01:48 Copyright (C) 2022 Microsoft Corporation

Я пытаюсь обновить значение в столбце, рассчитанном с использованием значения, которое также было обновлено во время того же процесса обновления, но в более ранней записи.

Думайте об этой таблице как об элементарной таблице безопасности. Начальные столбцы таблицы — это идентификатор RowID, уникальный PersonID, их имя, лицо, которому они подчиняются, и поле, в котором перечислены идентификаторы PersonID тех, кто может видеть их данные.

DROP TABLE IF EXISTS ##People1

CREATE TABLE ##People1
( 
    RowID      INT PRIMARY KEY IDENTITY(1, 1) NOT NULL, 
    PersonID   INT NULL, 
    PersonName VARCHAR(100) NULL, 
    ReportsTo  VARCHAR(100) NULL, 
    SeeMyData  VARCHAR(MAX) NULL
)
GO

INSERT INTO ##People1 (PersonID, PersonName, ReportsTo)
VALUES (7036, 'Liesl', NULL),
       (4049, 'Friedrich', 7036),
       (197, 'Louisa', 4049),
       (2303, 'Kurt', 197),
       (3409, 'Brigitta', 2303),
       (5686, 'Marta', 4049),
       (533, 'Gretl', 5686),
       (5204, 'Mike', 533),
       (4063, 'Sara', 3409),
       (1928, 'Tom', 197),
       (7013, 'Jerry', 1928),
       (7033, 'Sue', 533)
GO

В таблице выше второй человек, Фридрих, его персонид — 4049, и он подчиняется номеру 7036 Лисл. Лизл никому не отчитывается (вершина цепочки). Человеком, которому кто-то подчиняется, может быть любой человек, перед которым указан идентификатор человека.

Например, PersonID 1928, Том, подчиняется Луизе, personID 197. Человек, которому кто-то подчиняется, всегда будет находиться в более ранней строке.

Обратите внимание, что для поля SeeMyData нет значений. Цель состоит в том, чтобы вычислить и обновить строковое значение SeeMyData для каждого personID на основе этого строкового уравнения в псевдокоде:

SeeMyData = CONCAT(The SeeMyData string of the person they report to,'-', the PeopleID of the person they report to)

Результаты, которые я ищу:

идентификатор строки PeopleID Имя человека ОтчетыКому СмМоиДанные 1 7036 Лизл НУЛЕВОЙ НУЛЕВОЙ 2 4049 Фридрих 7036 7036 3 197 Луиза 4049 7036-4049 4 2303 Курт 197 7036-4049-197 5 3409 Бригитта 2303 7036-4049-197-2303 6 5686 Марта 4049 7036-4049 7 533 Гретл 5686 7036-4049-5686 8 5204 Майк 533 7036-4049-5686-533 9 4063 Сара 3409 7036-4049-197-2303-3409

И так далее.

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

Я попытался скопировать таблицу и использовать фрагмент кода, который нашел при поиске:

DROP TABLE IF EXISTS ##People2

SELECT * 
INTO ##People2 
FROM ##People1
GO

UPDATE ##People1
SET ##People1.seemydata = CONCAT(p2.SeeMyData, '-', p1.ReportsTo) 
FROM ##People1 p1
JOIN ##People2 p2 ON p1.reportsto = p2.PersonID

Проблема с этим кодом такая же, как и с другими решениями: фактическое значение SeeMyData, похоже, не записывается в столбец после расчета, поэтому следующая строка по-прежнему видит NULL в столбце SeeMyData.

Я также пытался использовать функцию LAG и вычислять разницу между RowID как порядковым номером, но у меня та же проблема: значения обновления для столбца SeeMyData на самом деле отсутствуют, и мы не будем знать, что такое RowID. поскольку это идентификационная строка.

Прошу прощения за проблемы с форматированием. Я думал, что это отформатирует блоки кода, но мне это не помогло.

Хранение таких вычисленных значений часто приводит к несогласованности. Вы рассматривали вид вместо этого? Или хотя бы используйте триггеры для управления столбцом.

jarlh 29.07.2024 18:49
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
1
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
DROP TABLE IF EXISTS ##People1;
CREATE TABLE ##People1
(
    RowID       INT  PRIMARY KEY IDENTITY(1, 1) NOT NULL, 
    PersonID    INT NULL, 
    PersonName VARCHAR(100) NULL, 
    ReportsTo  INT NULL,  -- Changed to INT to match PersonID type
    SeeMyData  VARCHAR(MAX) NULL
);
GO

INSERT INTO ##People1
(PersonID, PersonName, ReportsTo)
VALUES
(7036, 'Liesl',NULL),
(4049, 'Friedrich',7036),
(197,'Louisa',4049),
(2303,'Kurt',197),
(3409,'Brigitta',2303),
(5686,'Marta',4049),
(533,'Gretl',5686),
(5204,'Mike',533),
(4063,'Sara',3409),
(1928,'Tom',197),
(7013,'Jerry',1928),
(7033,'Sue',533);
GO

WITH HierarchyCTE AS
(
    SELECT 
        RowID,
        PersonID,
        PersonName,
        ReportsTo,
        CAST(NULL AS VARCHAR(MAX)) AS SeeMyData,
        CAST(CAST(PersonID AS VARCHAR(MAX)) AS VARCHAR(MAX)) AS Path
    FROM 
        ##People1
    WHERE 
        ReportsTo IS NULL

    UNION ALL

    SELECT 
        p.RowID,
        p.PersonID,
        p.PersonName,
        p.ReportsTo,
        CAST(NULL AS VARCHAR(MAX)) AS SeeMyData,
        CAST(h.Path + '-' + CAST(p.PersonID AS VARCHAR(MAX)) AS VARCHAR(MAX)) AS Path
    FROM 
        ##People1 p
    INNER JOIN 
        HierarchyCTE h ON p.ReportsTo = h.PersonID
)

UPDATE p
SET SeeMyData = CASE WHEN p.ReportsTo IS NULL THEN NULL ELSE SUBSTRING(h.Path, 1, LEN(h.Path) - LEN(CAST(p.PersonID AS VARCHAR(MAX))) - 1) END
FROM ##People1 p
INNER JOIN HierarchyCTE h ON p.RowID = h.RowID;


SELECT 
    *
FROM 
    ##People1
ORDER BY 
    RowID;
GO

Вы можете запустить sql здесь: SqlOnline

ИЛИ

DROP TABLE IF EXISTS ##People1;
CREATE TABLE ##People1
(
    RowID       INT PRIMARY KEY IDENTITY(1, 1) NOT NULL, 
    PersonID    INT NULL, 
    PersonName VARCHAR(100) NULL, 
    ReportsTo  INT NULL,
    SeeMyData  VARCHAR(MAX) NULL
);
GO

INSERT INTO ##People1
(PersonID, PersonName, ReportsTo)
VALUES
(7036, 'Liesl',NULL),
(4049, 'Friedrich',7036),
(197,'Louisa',4049),
(2303,'Kurt',197),
(3409,'Brigitta',2303),
(5686,'Marta',4049),
(533,'Gretl',5686),
(5204,'Mike',533),
(4063,'Sara',3409),
(1928,'Tom',197),
(7013,'Jerry',1928),
(7033,'Sue',533);
GO


DROP TABLE IF EXISTS #Hierarchy;

CREATE TABLE #Hierarchy
(
    RowID INT PRIMARY KEY,
    PersonID INT,
    PersonName VARCHAR(100),
    ReportsTo INT,
    SeeMyData VARCHAR(MAX),
    Path VARCHAR(MAX)
);


INSERT INTO #Hierarchy (RowID, PersonID, PersonName, ReportsTo, SeeMyData, Path)
SELECT 
    RowID,
    PersonID,
    PersonName,
    ReportsTo,
    NULL AS SeeMyData,
    CAST(PersonID AS VARCHAR(MAX)) AS Path
FROM 
    ##People1
WHERE 
    ReportsTo IS NULL;

-- Iteratively insert people who report to those already in the temporary table
DECLARE @RowCount INT = 1;

WHILE @RowCount > 0
BEGIN
    INSERT INTO #Hierarchy (RowID, PersonID, PersonName, ReportsTo, SeeMyData, Path)
    SELECT 
        p.RowID,
        p.PersonID,
        p.PersonName,
        p.ReportsTo,
        NULL AS SeeMyData,
        CAST(h.Path + '-' + CAST(p.PersonID AS VARCHAR(MAX)) AS VARCHAR(MAX)) AS Path
    FROM 
        ##People1 p
    INNER JOIN 
        #Hierarchy h ON p.ReportsTo = h.PersonID
    WHERE 
        NOT EXISTS (SELECT 1 FROM #Hierarchy WHERE RowID = p.RowID);

    SET @RowCount = @@ROWCOUNT;
END;

-- Update SeeMyData column
UPDATE p
SET SeeMyData = CASE WHEN p.ReportsTo IS NULL THEN NULL ELSE SUBSTRING(h.Path, 1, LEN(h.Path) - LEN(CAST(p.PersonID AS VARCHAR(MAX))) - 1) END
FROM ##People1 p
INNER JOIN #Hierarchy h ON p.RowID = h.RowID;

--output
SELECT 
    *
FROM 
    ##People1
ORDER BY 
    RowID;
GO
    

Это прекрасно работает! Однако мой реальный набор данных составляет более 1000 человек. Когда я добавляю OPTION (MAXRECURSION 0), сценарий работает более 10 минут, после чего я прекращаю выполнение. Даже когда я сократил его до 50 записей и установил для MAXRECURSION значение 30000, я так и не получил ответа. Удалив оператор MAXRECURSION и при 30 записях, я получаю правильные результаты. Есть ли способ сделать это с помощью временных таблиц?

user149104 30.07.2024 16:07

@user149104 user149104 Я добавил еще одно решение, которое использует временную таблицу и требует цикла while для итерации для генерации желаемого результата. Вы можете попробовать решение и, пожалуйста, примите ответ, если оно работает нормально. Спасибо

Jhonty 30.07.2024 18:56

Новый код, отличный от CTE, выполняется, но для тестовых данных все результирующие пути имеют значение NULL.

user149104 31.07.2024 16:17

Какие данные испытаний?

Jhonty 31.07.2024 17:28

Неважно, я скопировал его на новый запрос, и это сработало. Спасибо!

user149104 31.07.2024 19:47

У меня есть еще один вопрос, с которым мне нужна помощь. Я добавил к данным еще один столбец под названием DepartmentID. Я пытался получить код, который вы любезно предоставили, чтобы вместо добавления идентификатора ReportsTo к пути добавить DepartmentID. Я думал, что могу просто заменить каждое упоминание идентификатора ReportsTo идентификатором отдела, но, похоже, это не работает. В конечном итоге я хочу добавить оператор случая, в котором указывается, что если код отдела = 1, используйте ReportsToID, а если код отдела = 2, используйте код отдела в пути. Это возможно?

user149104 01.08.2024 02:32

Другие вопросы по теме