При обновлении строки, как также сохранить старые значения и обновить таблицы связывания? - PullRequest
0 голосов
/ 28 августа 2018

У меня есть две таблицы; ItemTable и ProductTable. Столбец ProductTable ItemID ссылается на столбец ItemTable ID. Столбцы ID обеих таблиц являются первичными ключами и столбцами идентификаторов.

Как это:

ItemTable:

ID    Col      ColOther    Latest    Time
100   'old'    'oldother'  1         <Autogenerated timestamp>


ProductTable:

ID    ItemID   Value   ValueOther   Latest   Time
12    100      'foo'   'bar'        1        <Autogenerated timestamp>

Всякий раз, когда я хочу вручную UPDATE строки в ItemTable, что обычно делается только одним запросом:

query = \
    """
    UPDATE ItemTable
    SET Col = ?, ColOther = ?
    WHERE ID = 100;
    """
cursor.execute(query, 'new', 'newother')

Вместо того, чтобы просто делать UPDATE, как описано выше, я хотел бы сделать эти вещи для ItemTable:

  • Автоматически UPDATE старый ряд, чтобы иметь Latest = 0
  • INSERT строка с обновленными значениями и Latest = 1 (скажем, эта строка получает ID 250)

Тогда для ProductTable:

  • Автоматически UPDATE старая строка связывания в ProductTable, имеющая Latest = 0
  • И автоматически INSERT в той же строке с новыми ItemID и Latest = 1

Эти автоматические INSERT и UPDATE предпочтительно выполняются только с простым запросом SQL (возможно, с использованием OUTPUT, но я не знаком с этим), или же его можно реализовать с помощью некоторого кода Python. Как мне это сделать?

Требуемый конечный результат:

ItemTable:

ID    Col      ColOther     Latest    Time
100   'old'    'oldother'   0         <Autogenerated timestamp>
250   'new'    'newother'   1         <Autogenerated timestamp>


ProductTable:

ID    ItemID   Value   ValueOther   Latest   Time
12    100      'foo'   'bar'        0        <Autogenerated timestamp>
110   250      'foo'   'bar'        1        <Autogenerated timestamp>

Ответы [ 3 ]

0 голосов
/ 07 мая 2019

Триггер ниже будет делать то, что вы хотите:

CREATE TRIGGER ItemTable_OnUpdate
ON ItemTable
INSTEAD OF UPDATE
AS
BEGIN
    DECLARE @newId int, @oldId int


    INSERT INTO ItemTable(Col, ColOther, Latest)
    SELECT Col, ColOther, 1 FROM INSERTED

    --get old and new ids
    SELECT @newId=@@IDENTITY, @oldId=ID FROM INSERTED

    UPDATE ItemTable SET Latest=0 WHERE ID=@oldId


    --updating ProductTable
    INSERT INTO ProductTable (ItemID, [Value], ValueOther, Latest)
    SELECT @newId, [Value], ValueOther, 1 FROM ProductTable WHERE ItemID=@oldId


    UPDATE ProductTable SET Latest=0 WHERE ItemID=@oldId
END;

После создания триггера любое обновление вставит новую запись в ItemTable, обновите пред. с помощью Latest = 0 и обновите дочернюю таблицу согласно вашему требованию.

Ниже приведен мой вывод после обновления ниже:

UPDATE ItemTable SET col='new', ColOther='newother' WHERE ID=12;

ItemTable:

ID  Col   ColOther  Latest  Time
12  old   oldother  0       0x00000000000007F0
13  new   newother  1       0x00000000000007EF

ProductTable:

ID  ItemID  Value   ValueOther  Latest  Time
18  12      foo     bar         0       0x00000000000007F2
19  13      foo     bar         1       0x00000000000007F1

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

create TRIGGER ItemTable_OnUpdate
ON ItemTable
INSTEAD OF UPDATE
AS
BEGIN

    Declare @s table( IdNew int,IdOld int)


MERGE INTO ItemTable AS dest
USING INSERTED AS ins ON 1=0   -- always false
    WHEN NOT MATCHED BY TARGET          -- happens for every row, because 1 is never 0
        THEN 
            INSERT (Col, ColOther, Latest)
                     VALUES (Col, ColOther, Latest)
            OUTPUT inserted.ID, ins.ID INTO @s (IdNew, IdOld);


UPDATE ItemTable SET Latest=0 WHERE ID in (select IdOld from @s)


    --updating ProductTable
    INSERT INTO ProductTable (ItemID, [Value], ValueOther, Latest)
    SELECT s.IdNew, pt.[Value], pt.ValueOther, 1 FROM @s s
        inner join ProductTable pt on pt.ItemID=s.IdOld


    UPDATE ProductTable SET Latest=0 WHERE ItemID in (select IdOld from @s)
END;
0 голосов
/ 07 мая 2019

Если вы используете современную версию SQL Server (т.е.> = SQL Server 2016 SP1)

Рассматривали ли вы использовать Временную таблицу для этого? Вы можете прочитать больше о Microsoft BOL

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

0 голосов
/ 28 августа 2018

Используйте ВЫХОД для этого:

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

Я переписал свой ответ после изменения вопроса:

-- create test tables and test data
CREATE table item
(ID int identity, 
Col varchar(99), 
ColOther varchar(99),
Latest bit default 1,
Time datetime default getdate())

INSERT item(col, colother) 
values('old','oldother')

CREATE table product
(ID int identity,
ItemID int,
[Value] varchar(20),
ValueOther varchar(20),
Latest bit default 1,
Time datetime default getdate())

INSERT product(itemid, [value], valueother)
values(1, 'foo', 'bar')

go
-- create procedure
CREATE procedure p_insert
(
  @id int,
  @col varchar(99),
  @colOther varchar(99)
)
as
BEGIN tran t
DECLARE @out table(IDold int, IDnew int)
INSERT item(col, colother, Latest)
OUTPUT @id, inserted.id INTO @out
SELECT @col, @colother, 1
FROM item
WHERE 
  id = @id
  and latest = 1

UPDATE i
SET Latest=0
FROM item i
JOIN @out o 
ON o.IDold = i.id
and i.Latest=1

DECLARE @p table
(itemid int,
[value] varchar(20),
valueother varchar(20))

UPDATE p
SET Latest=0
OUTPUT o.IDnew, deleted.[value],deleted.[valueother] 
INTO @p
FROM product p
JOIN @out o
ON p.ItemID = o.IDold
WHERE p.Latest=1

INSERT product(itemid, [value], valueother,  Latest)
SELECT itemid, value, valueother, 1
FROM @p
commit tran t

Чтобы проверить это:

exec p_insert 1, 'a','b'

Обратите внимание, что это будет работать, только если вы попытаетесь обновить существующую строку, где Latest = 1.

...