更新行时,如何还保留旧值并更新链接表?

ruo*_*ola 3 python sql t-sql sql-server pyodbc

我有两个桌子。ItemTableProductTableProductTableItemID列链接到ItemTableID列。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>
Run Code Online (Sandbox Code Playgroud)

每当我想手动UPDATE在中排一行时ItemTable,通常只需一个查询即可完成:

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

UPDATE我不仅要执行上述操作,还要做以下这些事情ItemTable

  • 自动UPDATE将旧行Latest = 0
  • INSERT具有更新值的行,并且Latest = 1(假设此行获得ID250)

然后为ProductTable

  • 自动UPDATE将ProductTable中的旧链接行包含Latest = 0
  • 自动INSERT将新行ItemIDLatest = 1

 

这种自动的INSERT并且UPDATE最好仅通过纯SQL查询(也许使用OUTPUT,但我不熟悉)进行,或者可以用一些Python代码实现。我将如何去做呢?

所需的最终结果:

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

t-c*_*.dk 6

为此使用OUTPUT

我不会为此使用触发器,因为在触发器中进行大量活动会导致在发生其他更改时发生意外的事情。相反,我正在使用存储过程

问题更改后,我重写了答案:

-- 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
Run Code Online (Sandbox Code Playgroud)

要对此进行测试:

exec p_insert 1, 'a','b'
Run Code Online (Sandbox Code Playgroud)

请注意,这仅在您尝试更新存在Latest = 1的现有行时才有效。


Ali*_*awi 5

下面的触发器将执行您想要的操作:

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;
Run Code Online (Sandbox Code Playgroud)

创建触发器后,任何更新将在中插入新记录ItemTable,更新上一个。使用Latest = 0,并根据您的要求更新子表。

以下是我在以下更新后的输出:

UPDATE ItemTable SET col='new', ColOther='newother' WHERE ID=12;
Run Code Online (Sandbox Code Playgroud)

项目表:

ID  Col   ColOther  Latest  Time
12  old   oldother  0       0x00000000000007F0
13  new   newother  1       0x00000000000007EF
Run Code Online (Sandbox Code Playgroud)

产品表:

ID  ItemID  Value   ValueOther  Latest  Time
18  12      foo     bar         0       0x00000000000007F2
19  13      foo     bar         1       0x00000000000007F1
Run Code Online (Sandbox Code Playgroud)

对于多行更新,我们可以使用以下触发器,因为上面的触发器被设计为请求者指定的更新(一行)的参考。我相信下面将处理多行更新。请尝试一下。

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;
Run Code Online (Sandbox Code Playgroud)