我一直在四处寻找这个问题的答案,但那里有一些相互矛盾或模棱两可的信息,发现很难找到确定的答案。
我的上下文:我在 node.js 中使用 'mssql' npm 包。我的 SQL 服务器是 Microsoft SQL Server 2014。
我有一条记录,它可能已经存在于表中,也可能不存在——如果存在,我想更新它,否则我想插入它。我不确定最佳 SQL 是什么,或者是否应该在 mssql 中运行某种“事务”。我发现了一些看起来不错的选项,但我不确定其中的任何一个:
选项 1: 如何更新(如果存在或插入)
问题是我什至不确定这是 MSSQL 中的有效语法。不过我确实喜欢它,而且它似乎也支持我喜欢的同时执行多行。
INSERT INTO table (id, user, date, points)
VALUES (1, 1, '2017-03-03', 25),
(2, 1, '2017-03-04', 25),
(3, 2, '2017-03-03', 100),
(4, 2, '2017-03-04', 150)
ON DUPLICATE KEY UPDATE points = VALUES(points)
Run Code Online (Sandbox Code Playgroud)
选项 2:
不知道这个有没有问题,只是不确定它是否是最佳的。似乎不支持多个同时行
update test set name='john' where id=3012
IF @@ROWCOUNT=0
insert into test(name) values('john');
Run Code Online (Sandbox Code Playgroud)
选项 3:合并,https : //dba.stackexchange.com/questions/89696/how-to-insert-or-update-using-single-query
有人说这个有点bug什么的?这显然也支持一次我喜欢的多个。
MERGE dbo.Test WITH (SERIALIZABLE) AS T
USING (VALUES (3012, 'john')) AS U (id, name)
ON U.id = T.id
WHEN MATCHED THEN
UPDATE SET T.name = U.name
WHEN NOT MATCHED THEN
INSERT (id, name)
VALUES (U.id, U.name);
Run Code Online (Sandbox Code Playgroud)
Mil*_*ney 13
如果您的系统是高度并发的,并且性能很重要 - 如果更新比插入更常见,您可以尝试以下模式:
BEGIN TRANSACTION;
UPDATE dbo.t WITH (UPDLOCK, SERIALIZABLE) SET val = @val WHERE [key] = @key;
IF @@ROWCOUNT = 0
BEGIN
INSERT dbo.t([key], val) VALUES(@key, @val);
END
COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)
参考: https: //sqlperformance.com/2020/09/locking/upsert-anti-pattern
另请阅读: https: //michaeljswart.com/2017/07/sql-server-upsert-patterns-and-antipatterns/
如果插入更常见:
BEGIN TRY
INSERT INTO dbo.AccountDetails (Email, Etc) VALUES (@Email, @Etc);
END TRY
BEGIN CATCH
-- ignore duplicate key errors, throw the rest.
IF ERROR_NUMBER() IN (2601, 2627)
UPDATE dbo.AccountDetails
SET Etc = @Etc
WHERE Email = @Email;
END CATCH
Run Code Online (Sandbox Code Playgroud)
我不会使用合并,虽然大多数错误显然已经修复 - 我们在生产之前遇到了重大问题。
编辑 - -
是的,上面的答案是针对单行的 - 对于多行,你会做这样的事情:尽管锁定背后的想法是相同的
BEGIN TRANSACTION;
UPDATE t WITH (UPDLOCK, SERIALIZABLE)
SET val = tvp.val
FROM dbo.t AS t
INNER JOIN @tvp AS tvp
ON t.[key] = tvp.[key];
INSERT dbo.t([key], val)
SELECT [key], val FROM @tvp AS tvp
WHERE NOT EXISTS (SELECT 1 FROM dbo.t WHERE [key] = tvp.[key]);
COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)
他们每个人都有不同的目的,优点和缺点。
选项 1 适用于多行插入/更新。但是它只检查主键约束。
选项 2 适用于小数据集。单个记录插入/更新。它更像是脚本。
选项 3 最适合大型查询。可以说,从一个表中读取并相应地插入/更新到另一个表。您可以定义插入和/或更新要满足的条件。您不仅限于主键/唯一约束。
| 归档时间: |
|
| 查看次数: |
2852 次 |
| 最近记录: |