[sql] SQL Server에 업데이트 저장 프로 시저 삽입

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

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

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

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

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



답변

귀하의 가정이 맞습니다. 이것이 최선의 방법이며 upsert / merge 라고 합니다.

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

위에서 언급 한 경우의 모든 업데이트에 대해 EXISTS 대신 UPSERT를 사용하면 테이블에서 하나의 추가 읽기를 제거합니다. 안타깝게도 Insert의 경우 UPSERT 및 IF EXISTS 메서드는 모두 테이블에서 동일한 수의 읽기를 사용합니다. 따라서 존재 확인은 추가 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의 새로운 기능 중 하나입니다.


답변

트랜잭션에서 실행해야 할뿐만 아니라 높은 격리 수준도 필요합니다. 사실 기본 격리 수준은 Read Commited이며이 코드에는 Serializable이 필요합니다.

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가 있는지 알고 있다고 가정하므로 모든 상황에서 작동하지는 않지만 그렇게 할 경우 실행을 절반으로 줄입니다.