使用 TRY/CATCH 执行 INSERT/UPDATE

Using TRY / CATCH to perform INSERT / UPDATE

我在许多存储过程中都有这种模式

-- Table1
[id] [int] IDENTITY(1,1) NOT NULL
[data] [varchar](512) NULL
[count] INT NULL

-- 'data' is unique, with a unique index on 'data' in 'Table1'
BEGIN TRY 
    INSERT INTO Table1 (data, count) SELECT @data,1;
END TRY
BEGIN CATCH
    UPDATE Table1 SET count = count + 1 WHERE data = @data;
END CATCH

我因使用此模式而受到抨击 before

You should never have exception "catching" in your normal logic flow. (Thus why it is called an "exception"..it should be exceptional (rare). Put a exists check around your INSERT. "if not exists (select null from Data where data = @data) begin /* insert here */ END

但是,在这种情况下我看不到解决方法。考虑以下替代方法。

INSERT INTO Table1 (data,count) 
SELECT @data,1 WHERE NOT EXISTS 
    (SELECT 1 FROM Table1 WHERE data = @data)

如果我这样做,这意味着每个插入都是唯一的,但我不能 'catch' 更新条件。

DECLARE @id INT;  
SET @id = (SELECT id FROM Table1 WHERE data = @data)

IF(@id IS NULL)
    INSERT INTO Table1 (data, count) SELECT @data,1;
ELSE 
    UPDATE Table1 SET count = count + 1 WHERE data = @data;

如果我这样做,我在检查和插入之间有一个竞争条件,所以我可以插入重复项。

BEGIN TRANSACTION
   DECLARE @id INT;  
   SET @id = (SELECT id FROM Table1 WHERE data = @data)

   IF(@id IS NULL)
       INSERT INTO Table1 (data, count) SELECT @data,1;
   ELSE 
       UPDATE Table1 SET count = count + 1 WHERE data = @data;
END TRANSACTION

如果我将其包装在 TRANSACTION 中,则会增加更多开销。我知道 TRY/CATCH 也会带来开销,但我 认为 TRANSACTION 会增加更多 - 有人知道吗?

人们一直告诉我在正常的应用程序逻辑中使用 TRY/CATCHBAD,但不会告诉我 为什么

注意:我 运行 SQL Server 2005 至少在一个盒子上,所以我 不能使用 MERGE

尝试更新,如果失败 - 插入新的。

BEGIN TRANSACTION
    UPDATE t
    SET
            t.count = t.count + 1
    FROM Table1 t
    WHERE t.data = @data

    IF (@@ROWCOUNT = 0)
    BEGIN
        INSERT INTO Table1
        (data, count)
        VALUES
        (@data, 1)
    END
COMMIT TRANSACTION

显式事务是为了解决并发问题而有条件地 INSERT/UPDATE 开展业务的成本。下面的示例使用锁定提示来避免与此代码的竞争条件。

BEGIN TRANSACTION;

INSERT  INTO Table1
        ( data
        , count
        )
        SELECT  @data
              , 1
        WHERE   NOT EXISTS ( SELECT 1
                             FROM   Table1 WITH ( UPDLOCK, HOLDLOCK )
                             WHERE  data = @data );

IF @@ROWCOUNT = 0
    UPDATE  Table1
    SET     count = count + 1
    WHERE   data = @data;

COMMIT;

如果更常见的路径是 UPDATE,请先尝试,然后是条件 INSERT