使用存储过程的具有唯一约束的SQL插入

时间:2013-09-06 03:27:59

标签: sql stored-procedures insert sql-server-2008-r2 unique-constraint

这是一个具体例子的通用问题。

我有一个包含三个字段的表(genreID(PK IDENTITY),流派和subGenre)。该表对(genre,subGenre)组合有唯一约束。

我想知道如果表中不存在存储过程,我该如何修改存储过程,否则返回现有类型的genreID(如果存在的话)。

CREATE PROCEDURE spInsertGenre
    @genreID int OUTPUT,
    @genre varchar(100),
    @subGenre varchar(100)= NULL
AS
BEGIN
    INSERT INTO Genre
    (
        genre,
        subGenre
    )
    Values (
        @genre,
        @subGenre
    )

    SELECT @genreID = SCOPE_IDENTITY()
END
GO

2 个答案:

答案 0 :(得分:5)

在执行插入之前,您可以尝试选择SP将插入的行:

CREATE PROCEDURE spInsertGenre
    @genreID int OUTPUT,
    @genre varchar(100),
    @subGenre varchar(100)= NULL
AS
BEGIN
    -- if the row to be inserted already exists, put the genreID into the @genreID output parameter 
    SELECT @genreID = genreID
    FROM Genre 
    WHERE genre = @genre 
    AND subGenre = @subGenre

    IF @genreID IS NULL -- if the genreID was not found, do an insert and select the new genreID to the @genreID output parameter
    BEGIN
        INSERT INTO Genre
        (
            genre,
            subGenre
        )
        Values (
            @genre,
            @subGenre
        )

        SELECT @genreID = SCOPE_IDENTITY()
    END
END
GO

答案 1 :(得分:1)

编辑:根据Dan Guzman,MERGE statement has the same problem of conditional INSERT/UPDATE race condition。您需要使用HOLDLOCK表提示来避免此问题。

您可以使用带有HOLDLOCK表提示的MERGE语句:

CREATE PROCEDURE spInsertGenre
    @GenreID int OUTPUT,
    @Genre varchar(100),
    @SubGenre varchar(100)= NULL
AS
BEGIN
    DECLARE @IDs TABLE (GenreID INT PRIMARY KEY);
    MERGE INTO Genre WITH (HOLDLOCK) AS g
    USING (VALUES(@Genre,@SubGenre)) p(Genre,SubGenre) ON g.Genre = p.Genre AND g.SubGenre = p.SubGenre
    WHEN NOT MATCHED 
        THEN 
        INSERT (Genre,SubGenre) 
        VALUES (p.Genre, p.SubGenre)
    OUTPUT inserted.GenreID INTO @IDs (GenereID);

    -- Above assigment is safe because this SP tries to insert only one row
    SELECT  @genreID = GenereID 
    FROM    @IDs;
END
GO

或者,如果要插入多行,则可以使用表参数:

-- This table type is used to store the rows which should be inserted
CREATE TYPE dbo.GenreRows AS TABLE 
(
    Genre varchar(100),
    SubGenre varchar(100),
    PRIMARY KEY (Genre,SubGenre)
);
GO

CREATE PROCEDURE spInsertGenre
(
    @pRows dbo.GenreRows READONLY -- p = parameter
)
AS
BEGIN
    DECLARE @IDs TABLE(GenreID INT PRIMARY KEY);
    MERGE INTO Genre WITH (HOLDLOCK) AS g
    USING @pRows p ON g.Genre = p.Genre AND g.SubGenre = p.SubGenre
    WHEN NOT MATCHED 
        THEN 
        INSERT (Genre,SubGenre) 
        VALUES (p.Genre, p.SubGenre)
    OUTPUT inserted.GenreID INTO @IDs (GenereID);

    SELECT  GenereID 
    FROM    @IDs;
END
GO