0

这里有一篇文章说这是一个坏主意,并且我必须始终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)

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

但是,在链接的帖子中,@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)

或者也许做一些花哨的主键。这里的响应暗示,如果我知道这确实被用作一项功能,我可以继续使用该IGNORE_DUP_KEY选项,一切都会好起来的。就我而言,建议的路径是什么?

4

1 回答 1

1

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

  1. 绝对有唯一的约束来保护最基本的数据。
  2. 在盲目插入行之前检查潜在的违规行为。
  3. 将 TRY/CATCH 包裹在最终插入周围,以保护用户免受检查失败的异常。

对于单行插入,这可以很简单:

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

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

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

  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
于 2012-07-02T01:24:17.047 回答