Mat*_*att 6 sql sql-server stored-procedures sql-server-2005 upsert
我正在尝试实现您的基本UPSERT功能,但有一点扭曲:有时我不想实际更新现有行.
本质上我正在尝试在不同的存储库之间同步一些数据,并且Upsert函数似乎是要走的路.所以主要基于Sam Saffron对这个问题的回答,以及其他一些研究和阅读,我提出了这个存储过程:
(注意:我正在使用MS SQL Server 2005,因此MERGE语句不是一个选项)
CREATE PROCEDURE [dbo].[usp_UpsertItem]
-- Add the parameters for the stored procedure here
@pContentID varchar(30) = null,
@pTitle varchar(255) = null,
@pTeaser varchar(255) = null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRANSACTION
UPDATE dbo.Item WITH (SERIALIZABLE)
SET Title = @pTitle,
Teaser = @pTeaser
WHERE ContentID = @pContentID
IF @@rowcount = 0
INSERT INTO dbo.Item (ContentID, Title, Teaser)
VALUES (@pContentID, @pTitle, @pTeaser)
COMMIT TRANSACTION
END
Run Code Online (Sandbox Code Playgroud)
对于基本的Upsert我很满意,但是我想让实际的更新以另一列的值为条件.可以将其视为"锁定"一行,以便Upsert过程不会进行进一步的更新.我可以像这样更改UPDATE语句:
UPDATE dbo.Item WITH (SERIALIZABLE)
SET Title = @pTitle,
Teaser = @pTeaser
WHERE ContentID = @pContentID
AND RowLocked = false
Run Code Online (Sandbox Code Playgroud)
但是当后续插入将尝试插入已存在但由于"已锁定"而未更新的行时,后续插入将因唯一约束违规(针对ContentID字段)而失败.
那么这是否意味着我不再拥有经典的Upsert,即我每次都必须选择行来确定它是否可以更新或插入?我认为是这种情况,所以我想我真正要求的是帮助确保事务隔离级别正确,以便程序安全执行.
一个非常常见的问题.有些方法在高并发性下并不成功.这里描述和压力测试:
在这种情况下,仅仅编写一些代码是不够的,您需要将其暴露给高并发性.例如,我不确定我是否理解CptSkippy推荐的内容,但以下内容演示了如何进行压力测试.设置表和程序:
CREATE TABLE [dbo].[TwoINTs](
[ID] [int] NOT NULL,
[i1] [int] NOT NULL,
[i2] [int] NOT NULL,
[i3] [int] NOT NULL
);
CREATE PROCEDURE dbo.SaveTwoINTs(@ID INT, @i1 INT, @i2 INT)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT OFF;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
DECLARE @ret INT;
SET @ret=0;
BEGIN TRAN;
IF EXISTS(SELECT 1 FROM dbo.TwoINTs WHERE ID=@ID) BEGIN
UPDATE dbo.TwoINTs WITH (SERIALIZABLE)
SET i1=i1+@i1, i2=i2+@i2 WHERE ID=@ID;
SET @ret=@@ERROR;
END ELSE BEGIN
INSERT INTO dbo.TwoINTs(ID, i1, i2, i3)VALUES(@ID, @i1, @i2, @i1);
SET @ret=@@ERROR;
END;
COMMIT;
RETURN @ret;
END
GO
Run Code Online (Sandbox Code Playgroud)
设置两个执行该过程的循环:
CREATE PROCEDURE Testers.UpsertLoop1
AS
BEGIN
DECLARE @ID INT, @i1 INT, @i2 INT, @count INT, @ret INT;
SET @count = 0;
WHILE @count<50000 BEGIN
SELECT @ID = COALESCE(MAX(ID),0) + 1 FROM dbo.TwoInts;
EXEC @ret=dbo.SaveTwoINTs @ID, 1, 0;
SET @count = @count + 1;
END;
END;
GO
CREATE PROCEDURE Testers.UpsertLoop2
AS
BEGIN
DECLARE @ID INT, @i1 INT, @i2 INT, @count INT, @ret INT;
SET @count = 0;
WHILE @count<50000 BEGIN
SELECT @ID = COALESCE(MAX(ID),0) + 1 FROM dbo.TwoInts;
EXEC @ret=dbo.SaveTwoINTs @ID, 0, 1;
SET @count = @count + 1;
END;
END;
Run Code Online (Sandbox Code Playgroud)
在两个选项卡中执行这些过程,并亲眼看看您遇到了很多错误:
Testers.UpsertLoop1 --run in one tab
Testers.UpsertLoop1 --run in one tab
Msg 2601, Level 14, State 1, Procedure SaveTwoINTs, Line 15
Cannot insert duplicate key row in object 'dbo.TwoINTs' with unique index 'UNQ_TwoInts_ID'.
The statement has been terminated.
Run Code Online (Sandbox Code Playgroud)
按照我提供的链接查看在并发下实际工作的方法.
| 归档时间: |
|
| 查看次数: |
12520 次 |
| 最近记录: |