ruo*_*ola 3 python sql t-sql sql-server pyodbc
我有两个桌子。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>
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 = 0INSERT具有更新值的行,并且Latest = 1(假设此行获得ID250)然后为ProductTable:
UPDATE将ProductTable中的旧链接行包含Latest = 0INSERT将新行ItemID和Latest = 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)
为此使用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的现有行时才有效。
下面的触发器将执行您想要的操作:
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)