1

SQL Server 2012 を使用しています。一意の文字列をテーブルに挿入したいと考えています。一意の文字列の行 ID を常に返したいと思っています。さて、これは 2 つの方法で実現できます。

どのソリューションが最適ですか?

これは問題のテーブルです:

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、 aSELECTが発行されます。

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 に試行して失敗させるよりも、最初に違反をチェックする方がはるかに効率的であることがわかりました。失敗率は低い)。詳細はこちらこちら

パフォーマンスに加えて、制約に依存してエラーを発生させないもう 1 つの理由は、明日誰かがそれを変更またはドロップする可能性があることです。

于 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

最初に挿入してから、結果を取得します。データベースは内部でチェックを行います。とにかく重複キーのロジックを書く必要があるので、それを使用してください。このようにして、データベースに対して 2 回ではなく 1 回の呼び出しを行います。また、2 回の呼び出しの間に何が起こるか誰にもわかりません。

編集: Aaron Bertrand の実行結果に基づく - この回答は常に有効とは限りません。

于 2013-10-30T15:09:26.457 に答える