我总是使用类似于以下的东西来实现它:
INSERT INTO TheTable
SELECT
@primaryKey,
@value1,
@value2
WHERE
NOT EXISTS
(SELECT
NULL
FROM
TheTable
WHERE
PrimaryKey = @primaryKey)
...但是一旦加载,就会发生主键违规。这是唯一插入此表的语句。那么这是否意味着上述陈述不是原子的?
问题是这几乎不可能随意重建。
也许我可以将其更改为以下内容:
INSERT INTO TheTable
WITH
(HOLDLOCK,
UPDLOCK,
ROWLOCK)
SELECT
@primaryKey,
@value1,
@value2
WHERE
NOT EXISTS
(SELECT
NULL
FROM
TheTable
WITH
(HOLDLOCK,
UPDLOCK,
ROWLOCK)
WHERE
PrimaryKey = @primaryKey)
虽然,也许我使用了错误的锁或使用过多的锁定等等。
我在stackoverflow.com上看到了其他问题,答案是建议“IF(SELECT COUNT(*)... INSERT”等等),但我总是在(可能是不正确的)假设单个SQL语句会是原子的。
有没有人有任何想法?
答案 0 :(得分:56)
"JFDI"模式怎么样?
BEGIN TRY
INSERT etc
END TRY
BEGIN CATCH
IF ERROR_NUMBER() <> 2627
RAISERROR etc
END CATCH
说真的,这是最快且最没有锁定的并发,特别是在大量时。 如果UPDLOCK升级并且整个表被锁定怎么办?
第4课:在调整索引之前开发upsert proc时,我首先相信
If Exists(Select…)
行会触发任何项目并禁止重复。纳达。在短时间内有数千个重复项,因为相同的项目将在相同的毫秒内达到upsert并且两个事务都会看到不存在并执行插入。经过大量测试后,解决方案是使用唯一索引,捕获错误,然后重试允许事务查看行并执行更新而不是插入。
答案 1 :(得分:23)
我添加了最初不存在的HOLDLOCK。如果没有此提示,请忽略该版本。
就我而言,这应该足够了:
INSERT INTO TheTable
SELECT
@primaryKey,
@value1,
@value2
WHERE
NOT EXISTS
(SELECT 0
FROM TheTable WITH (UPDLOCK, HOLDLOCK)
WHERE PrimaryKey = @primaryKey)
另外,如果你真的想要更新一行(如果它存在)并插入(如果不存在),你可能会发现this question有用。
答案 2 :(得分:17)
您可以使用MERGE:
MERGE INTO Target
USING (VALUES (@primaryKey, @value1, @value2)) Source (key, value1, value2)
ON Target.key = Source.key
WHEN MATCHED THEN
UPDATE SET value1 = Source.value1, value2 = Source.value2
WHEN NOT MATCHED BY TARGET THEN
INSERT (Name, ReasonType) VALUES (@primaryKey, @value1, @value2)
答案 3 :(得分:1)
我不知道这是否是“官方”方式,但您可以尝试INSERT
,如果失败则退回UPDATE
。
答案 4 :(得分:1)
首先,对我们的男人@gbn大肆宣传他对社区的贡献。甚至无法解释我多久发现自己遵循他的建议。
无论如何,足够的粉丝。
稍微补充一下他的回答,或许是#34;增强&#34;它。对于那些像我一样的人,左边的感觉与<> 2627
场景中的操作不一致(并且不能选择空CATCH
)。我从technet找到了这个小金块。
BEGIN TRY
INSERT etc
END TRY
BEGIN CATCH
IF ERROR_NUMBER() <> 2627
BEGIN
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT @ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
RAISERROR (
@ErrorMessage,
@ErrorSeverity,
@ErrorState
);
END
END CATCH
答案 5 :(得分:-4)
我过去使用不同的方法做过类似的操作。首先,我声明一个变量来保存主键。然后我用select语句的输出填充该变量,该语句查找带有这些值的记录。然后我做和IF声明。如果主键为null,则执行insert,否则返回一些错误代码。
DECLARE @existing varchar(10)
SET @existing = (SELECT primaryKey FROM TABLE WHERE param1field = @param1 AND param2field = @param2)
IF @existing is not null
BEGIN
INSERT INTO Table(param1Field, param2Field) VALUES(param1, param2)
END
ELSE
Return 0
END