문제

레코드가 있으면 업데이트를 수행하고 그렇지 않으면 삽입을 수행하는 저장 프로시저를 작성했습니다.다음과 같이 보입니다.

update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)

이런 방식으로 작성하는 논리는 업데이트가 where 절을 사용하여 암시적 선택을 수행하고 0을 반환하면 삽입이 발생한다는 것입니다.

이 방법의 대안은 선택을 수행한 다음 반환된 행 수에 따라 업데이트 또는 삽입을 수행하는 것입니다.업데이트를 수행하면 2번의 선택(첫 번째 명시적 선택 호출과 업데이트 위치에 대한 두 번째 암시적 호출)이 발생하기 때문에 이는 비효율적이라고 생각했습니다.프로세스가 삽입을 수행한다면 효율성에는 차이가 없습니다.

내 논리가 여기에 들리나요?이것이 삽입과 업데이트를 저장된 proc에 결합하는 방법입니까?

도움이 되었습니까?

해결책

귀하의 가정은 옳습니다. 이것이 최적의 방법이며 이를 호출합니다. 업서트/병합.

UPSERT의 중요성 - sqlservercentral.com에서:

위에서 언급 한 사례의 모든 업데이트에 대해 우리는 존재하지 않고 Upsert를 사용하면 테이블에서 하나의 추가 읽기를 제거합니다.불행히도 인서트의 경우, 상향 및 존재하는 경우 메소드는 테이블에 동일한 수의 판독 값을 사용합니다.따라서 존재 검사는 추가 I/O를 정당화 할 매우 유효한 이유가있을 때만 수행해야합니다.최적화 된 일을하는 방법은 DB에서 최대한 읽을 수 없는지 확인하는 것입니다.

최선의 전략은 업데이트를 시도하는 것입니다.업데이트의 행에 행이 영향을받지 않으면 삽입하십시오.대부분의 상황에서 행은 이미 존재하며 하나의 I/O 만 필요합니다.

편집하다:확인해 보세요 이 답변 링크된 블로그 게시물을 통해 이 패턴의 문제점과 이를 안전하게 작동시키는 방법을 알아볼 수 있습니다.

다른 팁

읽어 보시기 바랍니다 내 블로그에 게시 좋고 안전한 패턴을 사용할 수 있습니다.고려해야 할 사항이 많으며 이 질문에 대한 답변은 안전하지 않습니다.

빠른 답변을 얻으려면 다음 패턴을 시도해 보세요.SQL 2000 이상에서는 잘 작동합니다.SQL 2005는 다른 옵션을 여는 오류 처리를 제공하고 SQL 2008은 MERGE 명령을 제공합니다.

begin tran
   update t with (serializable)
   set hitCount = hitCount + 1
   where pk = @id
   if @@rowcount = 0
   begin
      insert t (pk, hitCount)
      values (@id,1)
   end
commit tran

SQL Server 2000/2005와 함께 사용하려면 원본 코드를 트랜잭션에 포함하여 동시 시나리오에서 데이터의 일관성을 유지해야 합니다.

BEGIN TRANSACTION Upsert
update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)
COMMIT TRANSACTION Upsert

이로 인해 추가 성능 비용이 발생하지만 데이터 무결성이 보장됩니다.

이미 제안한 대로 추가하려면 가능한 경우 MERGE를 사용해야 합니다.

그런데 MERGE는 SQL Server 2008의 새로운 기능 중 하나입니다.

트랜잭션에서 실행해야 할 뿐만 아니라 높은 격리 수준도 필요합니다.사실 기본 격리 수준은 읽기 커밋이며 이 코드에는 직렬화 가능이 필요합니다.

SET transaction isolation level SERIALIZABLE
BEGIN TRANSACTION Upsert
UPDATE myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
  begin
    INSERT into myTable (ID, Col1, Col2) values (@ID @col1, @col2)
  end
COMMIT TRANSACTION Upsert

@@error 검사와 롤백도 추가하는 것이 좋은 생각일 수 있습니다.

SQL 2008에서 병합을 수행하지 않는 경우 다음과 같이 변경해야 합니다.

@@rowcount = 0이고 @@error=0인 경우

그렇지 않고 어떤 이유로 업데이트가 실패하면 실패한 문의 행 개수가 0이기 때문에 나중에 삽입을 시도합니다.

UPSERT의 열렬한 팬으로 관리할 코드가 정말 줄어듭니다.내가하는 또 다른 방법은 다음과 같습니다.입력 매개변수 중 하나는 ID입니다. ID가 NULL 또는 0이면 INSERT이고, 그렇지 않으면 업데이트입니다.애플리케이션이 ID가 있는지 알고 있다고 가정하므로 모든 상황에서 작동하지는 않지만 그렇게 하면 실행 횟수가 절반으로 줄어듭니다.

귀하의 논리는 타당해 보이지만 특정 기본 키를 전달한 경우 삽입을 방지하기 위해 일부 코드를 추가하는 것이 좋습니다.

그렇지 않고 업데이트가 레코드에 영향을 주지 않은 경우 항상 삽입을 수행하는 경우 "UPSERT"가 실행되기 전에 누군가 레코드를 삭제하면 어떻게 됩니까?이제 업데이트하려는 레코드가 존재하지 않으므로 대신 레코드가 생성됩니다.아마도 당신이 찾고 있던 행동이 아닐 것입니다.

수정된 Dima Malenko 게시물:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 

BEGIN TRANSACTION UPSERT 

UPDATE MYTABLE 
SET    COL1 = @col1, 
       COL2 = @col2 
WHERE  ID = @ID 

IF @@rowcount = 0 
  BEGIN 
      INSERT INTO MYTABLE 
                  (ID, 
                   COL1, 
                   COL2) 
      VALUES      (@ID, 
                   @col1, 
                   @col2) 
  END 

IF @@Error > 0 
  BEGIN 
      INSERT INTO MYERRORTABLE 
                  (ID, 
                   COL1, 
                   COL2) 
      VALUES      (@ID, 
                   @col1, 
                   @col2) 
  END 

COMMIT TRANSACTION UPSERT 

오류를 포착하고 실패한 삽입 테이블에 레코드를 보낼 수 있습니다.
우리는 WSDL을 통해 전송되는 모든 데이터를 가져오고 가능하다면 내부적으로 수정하기 때문에 이 작업을 수행해야 했습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top