作者:aiminilee | 来源:互联网 | 2022-12-02 14:02
我的代码中有以下声明
INSERT INTO #TProductSales (ProductID, StockQTY, ETA1)
VALUES (@ProductID, @StockQTY, @ETA1)
我想做的事情如下:
IF @ProductID exists THEN
UPDATE #TProductSales
ELSE
INSERT INTO #TProductSales
有没有办法可以做到这一点?
1> Aaron Bertra..:
模式是(没有错误处理):
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE #TProductSales SET StockQty = @StockQty, ETA1 = @ETA1
WHERE ProductID = @ProductID;
IF @@ROWCOUNT = 0
BEGIN
INSERT #TProductSales(ProductID, StockQTY, ETA1)
VALUES(@ProductID, @StockQTY, @ETA1);
END
COMMIT TRANSACTION;
您无需在此处执行#temp表的其他读取操作.您已经通过尝试更新来做到这一点.为了防止竞争条件,你可以保护你想要隔离的两个或多个语句中的任何一个块:你将它包装在一个具有适当隔离级别的事务中(可能在这里可序列化,尽管只是当我们不讨论#temp表时,这是有意义的,因为根据定义,它是序列化的).
通过添加一个IF EXISTS
检查,您无需进一步提前(并且您需要添加锁定提示以使其安全/可序列化),但您可能会进一步落后,具体取决于您更新现有行与插入新行的次数.这可能会增加很多额外的I/O.
人们可能会告诉你使用MERGE
(实际上是幕后的多个操作,也需要用序列化保护),我劝你不要.我在这里列出原因:
对SQL Server的MERGE语句使用警告
对于多行模式(如TVP),我会以相同的方式处理这个问题,但是没有一种实用的方法可以避免第二次读取,就像单行情况一样.不,MERGE
也不避免.
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE t SET t.col = tvp.col
FROM dbo.TargetTable AS t
INNER JOIN @TVP AS tvp
ON t.ProductID = tvp.ProductID;
INSERT dbo.TargetTable(ProductID, othercols)
SELECT ProductID, othercols
FROM @TVP AS tvp
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.TargetTable
WHERE ProductID = tvp.ProductID
);
COMMIT TRANSACTION;
好吧,我想有办法做到这一点,但我还没有彻底测试过:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
DECLARE @exist TABLE(ProductID int PRIMARY KEY);
UPDATE t SET t.col = tvp.col
OUTPUT deleted.ProductID INTO @exist
FROM dbo.TargetTable AS t
INNER JOIN @tvp AS tvp
ON t.ProductID = tvp.ProductID;
INSERT dbo.TargetTable(ProductID, othercols)
SELECT ProductID, othercols
FROM @tvp AS t
WHERE NOT EXISTS
(
SELECT 1 FROM @exist
WHERE ProductID = t.ProductID
);
COMMIT TRANSACTION;
在任何一种情况下,您首先执行更新,否则您将更新刚刚插入的所有行,这将是浪费.
"人们可能会告诉你使用MERGE,我劝你不要" - 智慧的话语
关于Merge让我感到惊讶的是它很难修复.我的意思是其他SQL服务器TSQL构造有这么多问题?