使用SELECT重复检查还是依赖唯一索引?

时间:2013-10-30 15:05:36

标签: sql sql-server

使用SQL Server 2012.我想在表中插入唯一的字符串。我总是希望返回唯一字符串的行ID。现在,这可以通过两种方式实现。

哪种解决方案最好?

这是有问题的表格:

CREATE TABLE [dbo].[Comment](
    [CommentID] [int] IDENTITY(1,1) NOT NULL,
    [Comment] [nvarchar](256) NOT NULL

    CONSTRAINT [PK_Comment] PRIMARY KEY CLUSTERED([CommentID] ASC)
)

CREATE UNIQUE NONCLUSTERED INDEX [IX_Comment_Comment] ON [dbo].[Comment]
(
    [Comment] ASC
)

解决方案1 ​​

SELECT首先检查字符串是否存在。如果是,请返回ID。否则,INSERT新行并返回新创建的ID

CREATE PROCEDURE [dbo].[add_comment]
    @Comment [nvarchar](256)
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @CommentID [int]
    DECLARE @TransactionCount [int]

    BEGIN TRY
        SET @TransactionCount = @@TRANCOUNT

        IF @TransactionCount = 0
            BEGIN TRANSACTION

        SELECT @CommentID = [CommentID] FROM [dbo].[Comment] WHERE [Comment] = @Comment

        IF @@ROWCOUNT = 0
        BEGIN
            INSERT INTO [dbo].[Comment]([Comment]) VALUES (@Comment)

            SET @CommentID = SCOPE_IDENTITY()
        END

        IF @TransactionCount = 0
            COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        IF XACT_STATE() <> 0 AND @TransactionCount = 0 
            ROLLBACK TRANSACTION

        ; THROW
    END CATCH

    RETURN @CommentID
END

解决方案2

首先

INSERT。如果插入内容违反UNIQUE INDEX,则会发出SELECT

CREATE PROCEDURE [dbo].[add_comment2]
    @Comment [nvarchar](256)
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @CommentID [int]

    BEGIN TRY
        INSERT INTO [dbo].[Comment]([Comment]) VALUES (@Comment)
        SET @CommentID = SCOPE_IDENTITY()
    END TRY
    BEGIN CATCH
        IF @@ERROR = 2601 -- Duplicate
            SELECT @CommentID = [CommentID] FROM [dbo].[Comment] WHERE [Comment] = @Comment
        ELSE
            THROW
    END CATCH

    RETURN @CommentID
END
GO

解决方案3

想法? :)

5 个答案:

答案 0 :(得分:5)

在我的测试中,我发现首先检查违规而不是让SQL Server尝试失败更有效率,尤其是当预计失败率很高时(并且最好它们的性能大致相同) ,当故障率低时)。详情herehere

除了性能之外,不依赖于约束来引发错误的另一个原因是明天有人可能会改变或丢弃它。

答案 1 :(得分:1)

就我个人而言,这样做

INSERT INTO [dbo].[Comment]([Comment])
SELECT T.Comment
FROM (SELECT @Comment) T (Comment)
LEFT JOIN dbo.Comment C ON C.Comment = T.Comment
WHERE C.Comment IS NULL

SELECT @CommentID = CommentID
FROM dbo.Comment C
WHERE C.Comment = @Comment

答案 2 :(得分:1)

您还应该在事务中封装它。我喜欢条件插入的想法。

答案 3 :(得分:1)

我认为这是最简单的选择:

SET NOCOUNT ON;

BEGIN TRAN
    /* Insert if doesn't already exist */
    INSERT INTO dbo.comment (comment)
    SELECT @comment
    WHERE  NOT EXISTS (
             SELECT comment
             FROM   dbo.comment
             WHERE  comment = @comment
           );

    /* Return comment id */
    SELECT commentid
    FROM   dbo.comment
    WHERE  comment = @comment;
END TRAN

如果您更喜欢较新的MERGE语法(请先由Aaron Bertrand read this article ),请选择插入部分:

...
; WITH source AS (
  SELECT @comment As comment
)
MERGE INTO dbo.comment As destination
  USING source
    ON source.comment = destination.comment 
WHEN NOT MATCHED THEN
  INSERT (comment)
    VALUES (source.comment)
;
...

答案 4 :(得分:-2)

我会首先使用insert然后获取结果。 db将在内部进行检查。无论如何,您需要编写重复键的逻辑,因此请使用它。这样你就可以对数据库进行1次调用而不是2次。另外,谁知道2次调用之间会发生什么?

编辑:根据Aaron Bertrand的运行结果 - 这个答案并不总是有效。