我有一张表“INSERTIF”,看起来像这样 -
id value
S1 s1rocks
S2 s2rocks
S3 s3rocks
在向此表中插入行之前,我想检查给定的ID是否存在。如果它不存在,则插入。否则,只需更新值即可。我想以线程安全的方式做到这一点。你能告诉我我的代码是否正确吗?我尝试了它,它的工作原理。但是,我想确保我不会错过任何性能问题。
编辑1 - 我想使用此代码一次插入数百万行。每个insert语句都包含在我显示的代码中。
编辑2 - 我不想使用代码的UPDATE部分,只需插入即可。
我不想使用MERGE,因为它仅适用于SQL Server 2008及更高版本
感谢。
代码 -
-- no check insert
INSERT INTO INSERTIF(ID,VALUE)
VALUES('S1', 's1doesNOTrock')
--insert with checking
begin tran /* default read committed isolation level is fine */
if not exists
(select * from INSERTIF with (updlock, rowlock, holdlock)
where ID = 'S1')
BEGIN
INSERT INTO INSERTIF(ID,VALUE)
VALUES('S1', 's1doesNOTrock')
END
else
/* update */
UPDATE INSERTIF
SET VALUE = 's1doesNOTrock'
WHERE ID = 'S1'
commit /* locks are released here */
创建表格的代码 -
CREATE TABLE [dbo].[INSERTIF](
[id] [varchar](50) NULL,
[value] [varchar](50) NULL
)
INSERT [dbo].[INSERTIF] ([id], [value]) VALUES (N'S1', N's1rocks')
INSERT [dbo].[INSERTIF] ([id], [value]) VALUES (N'S2', N's2rocks')
INSERT [dbo].[INSERTIF] ([id], [value]) VALUES (N'S3', N's3rocks')
答案 0 :(得分:6)
您的问题是关于代码的线程安全性。简洁,不 - 它不是线程安全的。 (但请参阅下文讨论隔离的地方。)
由于您的“不存在”SELECT与相应操作之间的TOCTOU(检查时间,使用时间)问题,您有一个(小的)漏洞窗口。假设您在id
列上有一个唯一的(主要)键约束,您应该使用“更容易请求宽恕而不是权限”范例,而不是“先寻找你”之前的范例(请参阅EAFP vs LBYL)
这意味着您应该确定要使用的两个操作序列中的哪一个:
要么有效。如果工作主要是插入并偶尔更新,则1优于2;如果工作将主要通过偶尔插入更新,那么2优于1.你甚至可以自适应地工作;跟踪最后N行中发生的事情(其中N可能只有5或多达500),并使用启发式方法来决定尝试新行。如果INSERT失败(因为该行存在)仍然可能存在问题,但UPDATE不会更新任何内容(因为有人在插入失败后删除了行)。同样,UPDATE和INSERT也可能存在问题(没有行,但插入了一行)。
请注意,INSERT选项完全依赖于唯一约束,以确保不插入重复行; UPDATE选项更可靠。
您还需要考虑您的隔离级别 - 这可能会改变原始答案。如果您的隔离度足够高,以确保在执行“不存在”SELECT之后,没有其他人能够插入您确定不存在的行,那么您可能没问题。这对您的DBMS有了深刻的理解(我不是SQL Server专家)。
您还需要考虑交易边界;交易有多大,特别是如果源数据有一百万个条目。
答案 1 :(得分:1)
此技术通常称为UPSERT
。可以使用MERGE
在SQL Server中完成。
它的工作原理如下:
MERGE INTO A_Table
USING
(SELECT 'data_searched' AS Search_Col) AS SRC
-- Search predicates
--
ON A_Table.Data = SRC.Search_Col
WHEN MATCHED THEN
-- Update part of the 'UPSERT'
--
UPDATE SET
Data = 'data_searched_updated'
WHEN NOT MATCHED THEN
-- INSERT part of the 'UPSERT'
--
INSERT (Data)
VALUES (SRC.Search_Col);
另见http://www.sergeyv.com/blog/archive/2010/09/10/sql-server-upsert-equivalent.aspx
编辑:我看到你正在使用旧的SQL Server。在这种情况下,您必须使用多个语句。