(IGNORE_DUP_KEY = ON)或进行INSERT时进行独占检查?

时间:2012-07-02 00:44:58

标签: sql-server sql-server-2008 optimization

有一篇文章here说这是一个坏主意,而不是使用唯一约束,我必须始终INSERT只有数据不在数据库中。现在我的表结构如下:

CREATE TABLE [dbo].[TEST](
    [ColA] [varchar](255) NULL,
    [ColB] [datetime] NULL,
    [ColC] [datetime] NULL,
    [ColD] [int] NOT NULL,
) ON [PRIMARY]

对于我来说,只需要唯一的行,因此在设置唯一约束时有意义:

ALTER TABLE dbo.TEST
  ADD CONSTRAINT uniqueRows UNIQUE (ColA, ColB, ColC, ColD) WITH (IGNORE_DUP_KEY = ON)

当消耗新信息时,由于许多原因,可能会处理重复数据,因为我的代码不维护状态,因此至少对我来说忽略任何重复数据是有意义的。

然而,在他linked postanswer,@ PhilipKelley中,他说这是一个坏主意,应该按照以下方式进行检查:

INSERT INTO X 
VALUES(Y,Z)    
WHERE Y  NOT IN (SELECT Y FROM X)

在我的情况下转换为:

INSERT INTO dbo.TEST 
VALUES(ValA,ValB,ValC,ValD) 
WHERE (Some complicated check)

或者也许制作一些花哨的主键。响应here暗示如果我知道这确实被用作一个功能,我可以继续使用IGNORE_DUP_KEY选项,一切都会好的。在我的案例中建议的路径是什么?

1 个答案:

答案 0 :(得分:1)

我个人认为你应该做三件事:

  1. 绝对有唯一约束来保护最基本的数据。
  2. 在盲目插入行之前检查是否存在潜在的违规行为。
  3. 围绕最终插入包裹TRY / CATCH,以保护用户免受检查失败的异常。
  4. 对于单行插入,这可以简单如下:

    BEGIN TRY
      INSERT dbo.TEST(ValA, ValB, ValC, ValD)
        SELECT @ValA, @ValB, @ValC, @ValD
        WHERE NOT EXISTS
        (SELECT 1 FROM dbo.TEST
          WHERE ValA = @ValA AND ValB = @ValB AND ValC = @ValC AND ValD = @ValD);
    END TRY
    BEGIN CATCH
      PRINT 'Move along, nothing to see here...';
    END CATCH
    

    如果这些列中的任何一个或所有列都可以为空,那就会变得复杂一些。

    对于多行插入,您可以通过多种方式处理此问题。如果存在非唯一值(单独使用批处理或与表冲突),则可以使整个批处理失败,或者只允许成功的行进入表中。情况A:

      IF EXISTS (SELECT 1 FROM @SourceOfMultipleRows AS r
        WHERE EXISTS (SELECT 1 FROM dbo.Test AS t WHERE t.ValA = r.ValA AND ...))
      OR EXISTS (SELECT 1 FROM @SourceOfMultipleRows
        GROUP BY ValA, ValB, ValC, ValD HAVING COUNT(*) > 1)
      BEGIN
        PRINT 'Not proceeding at all.';
      END
      ELSE
      BEGIN
        BEGIN TRY
          INSERT dbo.TEST(ValA, ValB, ValC, ValD)
            SELECT ValA, ValB, ValC, ValD
            FROM @SourceOfMultipleRows AS r
            WHERE NOT EXISTS (SELECT 1 FROM dbo.Test AS t
              WHERE t.ValA = r.ValA AND ...)
            GROUP BY ValA, ValB, ValC, ValD;
        END TRY
        BEGIN CATCH
          PRINT 'Move along, nothing to see here...';
        END CATCH
      END
    

    方案B,您希望保留好行并忽略重复项:

    BEGIN TRY
      INSERT dbo.TEST(ValA, ValB, ValC, ValD)
        SELECT ValA, ValB, ValC, ValD
        FROM @SourceOfMultipleRows AS r
        WHERE NOT EXISTS (SELECT 1 FROM dbo.Test AS t
          WHERE t.ValA = r.ValA AND ...)
        GROUP BY ValA, ValB, ValC, ValD;
    END TRY
    BEGIN CATCH
      PRINT 'Move along...';
    END CATCH