相当于 SQL Server 中的 MySQL ON DUPLICATE KEY UPDATE
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27076348/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Equivalent of MySQL ON DUPLICATE KEY UPDATE in Sql Server
提问by Nemo
I am trying to find an equivalent of the following MySql query in Sql Server (2012)?
我正在尝试在 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);
Can anyone help?
任何人都可以帮忙吗?
PS. I have read that MERGE
query has similar function, but I find the syntax of that very different.
附注。我读过那个MERGE
查询有类似的功能,但我发现它的语法非常不同。
回答by SqlZim
You are basically looking for an Insert or Update patternsometimes referred to as an Upsert.
您基本上是在寻找有时称为Upsert的插入或更新模式。
I recommend this: Insert or Update pattern for Sql Server - Sam Saffron
我推荐这个:SQL Server 的插入或更新模式 - Sam Saffron
For a procedure that will be dealing with single rows, either these transactions would work well:
对于将处理单行的过程,这些事务都可以很好地工作:
Sam Saffron's First Solution (Adapted for this schema):
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's Second Solution (Adapted for this schema):
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
Even with a creative use of IGNORE_DUP_KEY
, you'd still be stuck having to use an insert/update block or a merge statement.
即使创造性地使用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 Spockshould do what you want.
Spock 提供的 Merge 答案应该可以满足您的需求。
Mergeisn't necessarily recommended. I use it, but I'd never admit that to @AaronBertrand.
不一定推荐合并。我使用它,但我永远不会向@AaronBertrand 承认这一点。
回答by Spock
Try this... I've added comments to try and explain what happens where in a SQL Merge statement. Source : MSDN : Merge Statement
试试这个... 我已经添加了注释来尝试解释 SQL Merge 语句中发生的情况。来源:MSDN:合并声明
The Merge Statement is different to the ON DUPLICATE KEY UPDATE statement in that you can tell it what columns to use for the merge.
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)
Hope that helps
希望有帮助
回答by Artjoman
Stored Procedure will save the day.
存储过程将节省一天。
Here I assume that COL_A and COL_B are unique columns and are type of INT NB! Don't have sql-server instance ATM so cannot guarantee correctness of the syntax. UPDATE! Here is a link to SQLFIDDLE
这里我假设 COL_A 和 COL_B 是唯一的列并且是 INT NB 类型!没有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
Then call this procedure with needed values instead of Update statement
然后使用所需的值而不是 Update 语句调用此过程
exec updateDuplicate 1, 1, 1, 2
GO
SELECT * from mytable
GO
回答by StuartLC
You can simulate a near identitical behaviour using an INSTEAD OF TRIGGER
:
您可以使用 模拟几乎相同的行为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;
How it works
这个怎么运作
- We first project a list of all rows being attempted to be inserted into the table into a #temp table, noting which of those ARE already in the underlying table via a
LEFT OUTER JOIN
on the key column(s)COL_D
which detect the duplication criteria. - We then need to repeat the actual work of an
INSERT
statement, by inserting those rows which are not already in the table (because of theINSTEAD OF
, we have removed the responsibility of insertion from the engine and need to do this ourselves). - Finally, we update all non-key columns in the matched rows with the newly 'inserted' data.
- 我们首先将尝试插入表中的所有行的列表投影到 #temp 表中,通过检测重复标准
LEFT OUTER JOIN
的键列上的a来注意哪些 ARE 已经在基础表中COL_D
。 - 然后我们需要
INSERT
通过插入那些不在表中的行来重复语句的实际工作(因为INSTEAD OF
,我们已经从引擎中删除了插入的责任,需要自己做这件事)。 - 最后,我们用新插入的数据更新匹配行中的所有非键列。
Salient Points
要点
- It works under the covers, i.e. any insert into the table while the trigger is enabled will be subject to the trigger (e.g. Application ORM, other stored procedures etc). The caller will generally be UNAWARE that the
INSTEAD OF
trigger is in place. - There must be a key of sorts to detect the duplicate criterion (natural or surrogate). I've assumed
COL_D
in this case, but it could be a composite key. (Key but cannot beIDENTITY
for obvious reasons, since the client wouldn't be inserting an Identity) - The trigger works for both single and multiple row INSERTS
- 它在幕后工作,即在启用触发器时对表的任何插入都将受触发器的约束(例如应用程序 ORM、其他存储过程等)。调用者通常不会意识到
INSTEAD OF
触发器已就位。 - 必须有一个键来检测重复的标准(自然或代理)。我假设
COL_D
在这种情况下,但它可能是一个复合键。(关键但不能IDENTITY
出于明显的原因,因为客户端不会插入身份) - 触发器适用于单行和多行 INSERTS
NB
NB
- The standard disclaimers with triggersapply, and more so with
INSTEAD OF
triggers - as this can cause surprising changes in observable behaviour of Sql Server, such as this - even well intendedINSTEAD OF
triggers can cause hours of wasted effort and frustration for developers and DBA's who are not aware of their presence on your table. - This will affect ALL inserts into the table. Not just yours.
回答by Suchit kumar
There's no DUPLICATE KEY UPDATE
equivalent in sql server,but you can use merged and when matched of sql server to get this done ,have a look here:
multiple operations using merge
DUPLICATE KEY UPDATE
在 sql server 中没有等价物,但是您可以使用合并和匹配的 sql server 来完成此操作,请查看此处:
使用合并的多个操作