使用TRY / CATCH执行INSERT / UPDATE

时间:2015-08-04 11:05:54

标签: sql-server exception try-catch upsert

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

-- 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

  

你的正常逻辑流程中永远不应该有“捕获”异常。 (因此它被称为“异常”...它应该是例外的(罕见)。在INSERT周围进行存在检查。“如果不存在(从Data中选择null,其中data = @data)begin / * insert here * / END

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

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

如果我这样做,则意味着每个插入都是唯一的,但我无法“捕获”更新条件。

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/CATCH BAD ,但不会告诉我为什么

注意:我在至少一个盒子上运行SQL Server 2005,因此我无法使用MERGE

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:0)

显式事务是使用条件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