1

使用 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 INDEXSELECT则发出 a 。

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

想法?:)

4

5 回答 5

5

在我的测试中,我发现首先检查违规比让 SQL Server 尝试并失败更有效,尤其是当失败率预计很高时(并且充其量它们的总体性能大致相同,当故障率低)。此处此处的详细信息。

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

于 2013-10-30T15:24:09.197 回答
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
于 2013-10-30T15:10:44.583 回答
1

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

于 2013-10-30T15:13:38.990 回答
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 的这篇文章):

...
; 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)
;
...
于 2013-10-30T15:22:22.113 回答
-2

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

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

于 2013-10-30T15:09:26.457 回答