1

이것은 구체적인 예를 들어 일반적인 질문입니다.저장 프로 시저를 사용하여 고유 제약 조건이있는 SQL 삽입

세 개의 필드 (genreID (PK IDENTITY), genre 및 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 

답변

4

당신은 삽입 전에 당신의 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

일을

당신은 따라서 HOLDLOCK 테이블 힌트 MERGE 문을 사용할 수 있습니다 또한 BEGIN TRANSACTION 및 COMMIT TRANSACTION에 배치하여 선택 및 삽입을 단일 트랜잭션 내에 배치해야합니다. 그렇지 않으면 저장 프로시 듀어가 동시에 여러 x 실행될 경우 중복 레코드가 가능합니다. –

+0

나는 시험했다 - 그것은 호기심있다. 그러나 ~ 5,000 레코드를 삽입 할 시간은 SELECT 문없이 16 초가 걸렸고 SELECT 문은 여전히 ​​16 초였다. --- 왜 그냥 손실이 없는지 궁금하다. 여기서 speed (0 레코드로 시작하는 테이블에 5k 레코드를 삽입하고 삽입하려고 시도한 중복이 없음) – Kairan

1

편집 : 댄 구즈, MERGE statement has the same problem of conditional INSERT/UPDATE race condition에 따라. 이 문제를 피하려면 HOLDLOCK 테이블 힌트를 사용해야합니다. 당신은 당신이 이렇게 테이블 매개 변수를 사용할 수 많은 행을 삽입 할 경우,

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 
+0

더 많은 인라인 주석을 추가하여 설명 할 수 있습니까? –

+0

@chrisFrisina : 높은 동시성 하에서'HOLDLOCK' 테이블 힌트가없는'MERGE'는 중복 키 예외를 생성 할 수 있습니다. Dan Guzman의 기사를 참조하십시오. –