developer tip

행이없는 경우에만 삽입

copycodes 2020. 11. 15. 11:25
반응형

행이없는 경우에만 삽입


나는 그것을 달성하기 위해 항상 다음과 비슷한 것을 사용했습니다.

INSERT INTO TheTable
SELECT
    @primaryKey,
    @value1,
    @value2
WHERE
    NOT EXISTS
    (SELECT
        NULL
    FROM
        TheTable
    WHERE
        PrimaryKey = @primaryKey)

...하지만 일단로드되면 기본 키 위반이 발생했습니다. 이것은이 테이블에 전혀 삽입하는 유일한 명령문입니다. 그렇다면 이것은 위의 진술이 원자가 아니라는 것을 의미합니까?

문제는 이것이 마음대로 다시 만드는 것이 거의 불가능하다는 것입니다.

아마도 다음과 같이 변경할 수 있습니다.

INSERT INTO TheTable
WITH
    (HOLDLOCK,
    UPDLOCK,
    ROWLOCK)
SELECT
    @primaryKey,
    @value1,
    @value2
WHERE
    NOT EXISTS
    (SELECT
        NULL
    FROM
        TheTable
    WITH
        (HOLDLOCK,
        UPDLOCK,
        ROWLOCK)
    WHERE
        PrimaryKey = @primaryKey)

비록 내가 잘못된 잠금 장치를 사용하거나 너무 많은 잠금 장치를 사용하고있을 수 있습니다.

나는 답변이 "IF (SELECT COUNT (*) ... INSERT"등을 제안하는 다른 질문을 stackoverflow.com에서 보았지만, 저는 항상 단일 SQL 문이 원자적일 것이라는 (아마도 틀린) 가정하에있었습니다.)

누구에게 아이디어가 있습니까?


무엇에 대해 "JFDI" 패턴?

BEGIN TRY
   INSERT etc
END TRY
BEGIN CATCH
    IF ERROR_NUMBER() <> 2627
      RAISERROR etc
END CATCH

진지하게, 이것은 특히 높은 볼륨에서 잠금없이 가장 빠르고 가장 동시 적입니다. UPDLOCK이 에스컬레이션되고 전체 테이블이 잠겨 있으면 어떻게됩니까?

4읽기 :

레슨 4 : 인덱스를 조정하기 전에 upsert proc을 개발할 때 먼저 If Exists(Select…)라인이 모든 항목에 대해 실행되고 중복을 금지 할 것이라고 믿었습니다 . 나다. 짧은 시간에 동일한 항목이 동일한 밀리 초에 upsert에 도달하고 두 트랜잭션 모두 존재하지 않음을 확인하고 삽입을 수행하기 때문에 수천 개의 중복이있었습니다. 많은 테스트 후 솔루션은 고유 인덱스를 사용하고 오류를 포착 한 다음 트랜잭션이 행을보고 삽입 대신 업데이트를 수행 할 수 있도록 재 시도하는 것이 었습니다.


원래 존재하지 않았던 HOLDLOCK을 추가했습니다. 이 힌트가없는 버전은 무시하십시오.

내가 아는 한 이것으로 충분할 것입니다.

INSERT INTO TheTable 
SELECT 
    @primaryKey, 
    @value1, 
    @value2 
WHERE 
    NOT EXISTS 
    (SELECT 0
     FROM TheTable WITH (UPDLOCK, HOLDLOCK)
     WHERE PrimaryKey = @primaryKey) 

또한 실제로 행이있는 경우 업데이트하고 그렇지 않은 경우 삽입하려는 경우이 질문이 유용 할 수 있습니다 .


MERGE를 사용할 수 있습니다.

MERGE INTO Target
USING (VALUES (@primaryKey, @value1, @value2)) Source (key, value1, value2)
ON Target.key = Source.key
WHEN MATCHED THEN
    UPDATE SET value1 = Source.value1, value2 = Source.value2
WHEN NOT MATCHED BY TARGET THEN
    INSERT (Name, ReasonType) VALUES (@primaryKey, @value1, @value2)

이것이 "공식적인"방법인지는 모르겠지만을 시도하고 실패하면 INSERT다시 돌아갈 수 UPDATE있습니다.


첫째, 커뮤니티에 대한 그의 공헌에 대해 @gbn 남자에게 큰 외침을 보냅니다. 내가 얼마나 자주 그의 충고를 따르는 지 설명 할 수 없다.

어쨌든 충분히 팬보이.

그의 대답에 약간 추가하려면 아마도 "강화"하십시오. 저와 같은 사람들에게는 <> 2627시나리오 에서해야 할 일에 대해 불안한 느낌이 들었습니다 (비어있는 CATCH것은 선택 사항이 아닙니다). 나는 technet 에서이 작은 너겟을 찾았다 .

    BEGIN TRY
       INSERT etc
    END TRY
    BEGIN CATCH
        IF ERROR_NUMBER() <> 2627
          BEGIN
                DECLARE @ErrorMessage NVARCHAR(4000);
                DECLARE @ErrorSeverity INT;
                DECLARE @ErrorState INT;

                SELECT @ErrorMessage = ERROR_MESSAGE(),
                @ErrorSeverity = ERROR_SEVERITY(),
                @ErrorState = ERROR_STATE();

                    RAISERROR (
                        @ErrorMessage,
                        @ErrorSeverity,
                        @ErrorState
                    );
          END
    END CATCH

I've done a similar operation in past using a different method. First, I declare a variable to hold the primary key. Then I populate that variable with the output of a select statement which looks for a record with those values. Then I do and IF statement. If primary key is null, then do insert, else, return some error code.

     DECLARE @existing varchar(10)
    SET @existing = (SELECT primaryKey FROM TABLE WHERE param1field = @param1 AND param2field = @param2)

    IF @existing is not null
    BEGIN
    INSERT INTO Table(param1Field, param2Field) VALUES(param1, param2)
    END
    ELSE
    Return 0
END

참고URL : https://stackoverflow.com/questions/3407857/only-inserting-a-row-if-its-not-already-there

반응형