相当于 Sql Server 中的 MySQL ON DUPLICATE KEY UPDATE
Posted
技术标签:
【中文标题】相当于 Sql Server 中的 MySQL ON DUPLICATE KEY UPDATE【英文标题】:Equivalent of MySQL ON DUPLICATE KEY UPDATE in Sql Server 【发布时间】:2015-01-20 11:36:23 【问题描述】:我正在尝试在 Sql Server (2012) 中找到以下 mysql 查询的等效项?
INSERT INTO mytable (COL_A, COL_B, COL_C, COL_D)
VALUES ( 'VAL_A','VAL_B', 'VAL_C', 'VAL_D')
ON DUPLICATE KEY UPDATE COL_D= VALUES(COL_D);
谁能帮忙?
附言。我读过MERGE
查询具有类似的功能,但我发现它的语法非常不同。
【问题讨论】:
请参阅this question 寻求帮助。 这也很有用:***.com/questions/1197733/… 您是否要求所有插入都具有此行为,还是仅针对特定插入? 这能回答你的问题吗? Does SQL Server Offer Anything Like MySQL's ON DUPLICATE KEY UPDATE 【参考方案1】:您基本上是在寻找 插入或更新模式,有时也称为 Upsert。
我推荐这个:Insert or Update pattern for Sql Server - Sam Saffron
对于将处理单行的过程,这些事务都可以正常工作:
Sam Saffron 的第一个解决方案(适用于此架构):
begin tran
if exists (
select *
from mytable with (updlock,serializable)
where col_a = @val_a
and col_b = @val_b
and col_c = @val_c
)
begin
update mytable
set col_d = @val_d
where col_a = @val_a
and col_b = @val_b
and col_c = @val_c;
end
else
begin
insert into mytable (col_a, col_b, col_c, col_d)
values (@val_a, @val_b, @val_c, @val_d);
end
commit tran
Sam Saffron 的第二个解决方案(适用于此架构):
begin tran
update mytable with (serializable)
set col_d = @val_d
where col_a = @val_a
and col_b = @val_b
and col_c = @val_c;
if @@rowcount = 0
begin
insert into mytable (col_a, col_b, col_c, col_d)
values (@val_a, @val_b, @val_c, @val_d);
end
commit tran
即使创造性地使用了 IGNORE_DUP_KEY
,您仍然不得不使用插入/更新块或合并语句。
update mytable
set col_d = 'val_d'
where col_a = 'val_a'
and col_b = 'val_b'
and col_c = 'val_c';
insert into mytable (col_a, col_b, col_c, col_d)
select 'val_a','val_b', 'val_c', 'val_d'
where not exists (select *
from mytable with (serializable)
where col_a = 'val_a'
and col_b = 'val_b'
and col_c = 'val_c'
);
The Merge answer provided by Spock 应该做你想做的事。
合并不一定是推荐的。我使用它,但我永远不会向@AaronBertrand 承认这一点。
Use Caution with SQL Server's MERGE Statement - Aaron Bertrand
Can I optimize this merge statement - Aaron Bertrand
If you are using indexed views and MERGE, please read this! - Aaron Bertrand
An Interesting MERGE Bug - Paul White
UPSERT Race Condition With Merge
【讨论】:
【参考方案2】:试试这个... 我添加了 cmets 来尝试解释 SQL Merge 语句中发生的情况。 来源:MSDN : Merge Statement
Merge 语句与 ON DUPLICATE KEY UPDATE 语句的不同之处在于您可以告诉它要用于合并的列。
CREATE TABLE #mytable(COL_A VARCHAR(10), COL_B VARCHAR(10), COL_C VARCHAR(10), COL_D VARCHAR(10))
INSERT INTO #mytable VALUES('1','0.1', '0.2', '0.3'); --<These are the values we'll be updating
SELECT * FROM #mytable --< Starting values (1 row)
MERGE #mytable AS target --< This is the target we want to merge into
USING ( --< This is the source of your merge. Can me any select statement
SELECT '1' AS VAL_A,'1.1' AS VAL_B, '1.2' AS VAL_C, '1.3' AS VAL_D --<These are the values we'll use for the update. (Assuming column COL_A = '1' = Primary Key)
UNION
SELECT '2' AS VAL_A,'2.1' AS VAL_B, '2.2' AS VAL_C, '2.3' AS VAL_D) --<These values will be inserted (cause no COL_A = '2' exists)
AS source (VAL_A, VAL_B, VAL_C, VAL_D) --< Column Names of our virtual "Source" table
ON (target.COL_A = source.VAL_A) --< This is what we'll use to find a match "JOIN source on Target" using the Primary Key
WHEN MATCHED THEN --< This is what we'll do WHEN we find a match, in your example, UPDATE COL_D = VALUES(COL_D);
UPDATE SET
target.COL_B = source.VAL_B,
target.COL_C = source.VAL_C,
target.COL_D = source.VAL_D
WHEN NOT MATCHED THEN --< This is what we'll do when we didn't find a match
INSERT (COL_A, COL_B, COL_C, COL_D)
VALUES (source.VAL_A, source.VAL_B, source.VAL_C, source.VAL_D)
--OUTPUT deleted.*, $action, inserted.* --< Uncomment this if you want a summary of what was inserted on updated.
--INTO #Output --< Uncomment this if you want the results to be stored in another table. NOTE* The table must exists
;
SELECT * FROM #mytable --< Ending values (2 row, 1 new, 1 updated)
希望有帮助
【讨论】:
【参考方案3】:您可以使用INSTEAD OF TRIGGER
模拟几乎相同的行为:
CREATE TRIGGER tMyTable ON MyTable
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
SELECT i.COL_A, i.COL_B, i.COL_C, i.COL_D,
CASE WHEN mt.COL_D IS NULL THEN 0 ELSE 1 END AS KeyExists
INTO #tmpMyTable
FROM INSERTED i
LEFT JOIN MyTable mt
ON i.COL_D = mt.COL_D;
INSERT INTO MyTable(COL_A, COL_B, COL_C, COL_D)
SELECT COL_A, COL_B, COL_C, COL_D
FROM #tmpMyTable
WHERE KeyExists = 0;
UPDATE mt
SET mt.COL_A = t.COL_A, mt.COL_B = t.COL_B, mt.COL_C = t.COL_C
FROM MyTable mt
INNER JOIN #tmpMyTable t
ON mt.COL_D = t.COL_D AND t.KeyExists = 1;
END;
SqlFiddle here
工作原理
我们首先将尝试插入表中的所有行的列表投影到#temp 表中,并通过键列COL_D
上的LEFT OUTER JOIN
记录其中哪些已经在基础表中检测重复标准。
然后我们需要重复INSERT
语句的实际工作,通过插入那些不在表中的行(因为INSTEAD OF
,我们已经从引擎中删除了插入的责任,需要自己做)。
最后,我们使用新“插入”的数据更新匹配行中的所有非键列。
重点
它在幕后工作,即启用触发器时对表的任何插入都将受到触发器的影响(例如应用程序 ORM、其他存储过程等)。调用者通常不知道INSTEAD OF
触发器已到位。
必须有某种类型的键来检测重复标准(自然或代理)。在这种情况下,我假设COL_D
,但它可能是一个复合键。 (密钥但不能是IDENTITY
,原因很明显,因为客户端不会插入身份)
触发器适用于单行和多行 INSERTS
注意
standard disclaimers with triggers 应用,INSTEAD OF
触发器更是如此 - 因为这可能会导致 Sql Server 的可观察行为发生令人惊讶的变化,例如这样 - 即使是精心设计的 INSTEAD OF
触发器也会导致数小时的浪费精力和挫败感适用于不知道自己在您的桌子上的开发人员和 DBA。
这将影响到表中的所有插入。不只是你的。
【讨论】:
我很惊讶这个选项没有更多的赞成票。这是在 MSSQL 中构建 Upsert 的一种创造性方式。我唯一关心的是查询的效率,考虑到行必须复制到临时表。如果插入影响了数十万或数百万行,那么这个触发器的效率有多高,需要先复制每个受影响的行? 几年后,由于性能以及触发器的标准“代码是隐藏原因”,我自己不会遵循自己的建议。基于集合的 MERGE 是进行有效 upserting 的正确方法 虽然我同意关注点分离的概念(代码是隐藏的),但这是一个很好的解决方法示例,用于在需要补偿我们没有的外部应用程序中的错误的情况下控制。【参考方案4】:存储过程将拯救这一天。
这里我假设 COL_A 和 COL_B 是唯一列并且是 INT 类型 注意!没有 sql-server 实例 ATM,因此无法保证语法的正确性。 更新!这是SQLFIDDLE的链接
CREATE TABLE mytable
(
COL_A int UNIQUE,
COL_B int UNIQUE,
COL_C int,
COL_D int,
)
GO
INSERT INTO mytable (COL_A, COL_B, COL_C, COL_D)
VALUES (1,1,1,1),
(2,2,2,2),
(3,3,3,3),
(4,4,4,4);
GO
CREATE PROCEDURE updateDuplicate(@COL_A INT, @COL_B INT, @COL_C INT, @COL_D INT)
AS
BEGIN
DECLARE @ret INT
SELECT @ret = COUNT(*)
FROM mytable p
WHERE p.COL_A = @COL_A
AND p.COL_B = @COL_B
IF (@ret = 0)
INSERT INTO mytable (COL_A, COL_B, COL_C, COL_D)
VALUES ( @COL_A, @COL_B, @COL_C, @COL_D)
IF (@ret > 0)
UPDATE mytable SET COL_D = @COL_D WHERE col_A = @COL_A AND COL_B = @COL_B
END;
GO
然后使用所需的值而不是 Update 语句调用此过程
exec updateDuplicate 1, 1, 1, 2
GO
SELECT * from mytable
GO
【讨论】:
您可以通过尝试更新记录来改进此解决方案,然后检查@@ROWCOUNT 是否等于0,如果等于,则可以插入记录。通过这种优化,您可以保存搜索(索引查找或索引扫描)。 同意,这将是更优雅的解决方案【参考方案5】:在 sql server 中没有 DUPLICATE KEY UPDATE
等价物,但是您可以使用合并和匹配的 sql server 来完成此操作,请看这里:
multiple operations using merge
【讨论】:
以上是关于相当于 Sql Server 中的 MySQL ON DUPLICATE KEY UPDATE的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server 相当于 MySQL 的 EXPLAIN