2008-08-17 5 views
89

레코드가 존재하면 업데이트 할 저장 프로 시저를 작성했습니다. 그렇지 않으면 삽입이 수행됩니다. 그것은 다음과 같은 :Insert update SQL에 저장된 proc 파일

update myTable set [email protected], [email protected] where [email protected] 
if @@rowcount = 0 
insert into myTable (Col1, Col2) values (@col1, @col2) 

내 로직이 방법을 쓰고 뒤에 업데이트가 암시가 어디에 절을 사용하여 선택을 수행하는 것입니다 그리고 그것은 0을 반환하는 경우 다음 삽입이 일어날 것이다.

이 방법을 사용하면 선택을 수행 한 다음 반환되는 행 수에 따라 업데이트 또는 삽입을 수행 할 수 있습니다. 이것은 비효율적 인 것으로 생각했는데, 업데이트를 수행하면 2 개의 선택 (첫 번째 명시 적 선택 호출과 두 번째 암시 적 업데이트가 발생 함)이 발생하기 때문입니다. proc이 삽입을하면 효율성에 차이가 없다.

내 논리가 여기에 들리는가요? 삽입 및 업데이트를 저장 프로 시저에 결합하는 방법입니까?

답변

56

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

Importance of UPSERT - from sqlservercentral.com

: 우리가 존재합니다 대신 UPSERT를 사용 경우 우리는 테이블에서 하나 개 추가 읽기를 제거하는 상기의 경우 모든 업데이트에 대한

. 불행히도 삽입 도구의 경우 UPSERT 및 IF EXISTS 메서드는 모두 테이블의 동일한 읽기 수인 을 사용합니다. 따라서 의 존재 확인은 추가 I/O를 정당화하는 매우 유효한 이유 인 이있는 경우에만 수행해야합니다. 에 최적화 된 방법은 에 DB가 거의 읽히지 않도록하는 것입니다.

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

편집 : 이 this answer을 확인하시기 바랍니다 및 링크 된 블로그 게시물이 패턴을 어떻게 안전하게 작동하게하는 방법의 문제에 대해 배울 수 있습니다.

+1

글쎄, 적어도 한 가지 질문에 대답했다고 생각합니다. 그리고 질문에있는 코드가 이미 저에게 맞았 기 때문에 코드를 추가하지 않았습니다. 트랜잭션에 넣을 수도 있지만 업데이트를 위해 격리 수준을 고려하지 않았습니다. 귀하의 답변에 그것을 지적 주셔서 감사합니다! – binOr

6

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

+0

그리고 당신은 절대적으로 그것을 사용하기보다는 오히려 그것을 사용해야한다. 좋은 예가 여기에 있습니다. - https://www.mssqltips.com/sqlservertip/1704/using-merge-in-sql-server-to-insert-update-and-delete-at-the-same-time/ –

8

SQL Server 2000/2005와 함께 사용하려면 원본 시나리오를 트랜잭션으로 묶어야 데이터가 동시 시나리오에서 일관되게 유지되어야합니다.

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

이렇게하면 추가 성능 비용이 발생하지만 데이터 무결성이 보장됩니다.

이미 제안 된대로 추가 할 경우 MERGE를 사용해야합니다.

3

UPSERT의 빅 팬은 실제로 관리 할 코드를 줄입니다. 내가하는 또 다른 방법은 다음과 같습니다. 입력 매개 변수 중 하나는 ID이고, ID가 NULL 또는 0이면 INSERT이고, 그렇지 않으면 업데이트입니다.ID가 있는지를 응용 프로그램이 알고 있다고 가정하므로 모든 상황에서 작동하지 않지만 실행하면 절반으로 줄입니다.

1

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

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

3

당신은 SQL 2008에서 병합을 수행하지 않는 경우에 당신은 그것을 변경해야합니다

경우 @@ 행 개수 = 0 @@ 오류 = 0

그렇지 않으면 업데이트는 다음 몇 가지 이유로 실패 할 경우 시도가 실패한 문장의 행 수는 0이므로

5

트랜잭션에서 실행할 필요가있을뿐만 아니라 높은 격리 수준도 필요합니다. 나는 사실 디폴트 분리 레벨은 Commited Read이며이 코드는 Serializable이 필요하다.

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

@@ 오류 확인 및 롤백을 추가하는 것이 좋습니다.

+0

@Munish Goyal 데이터베이스에서 여러 명령과 선행이 병렬로 실행되기 때문입니다. 그런 다음 다른 스레드가 업데이트가 실행 된 직후와 삽입이 실행되기 전에 행을 삽입 할 수 있습니다. –

46

좋은 안전 패턴을 찾으려면 post on my blog을 읽어보십시오. 많은 고려 사항이 있으며이 질문에 대한 대답은 안전하지 않습니다.

빠른 답을 얻으려면 다음 패턴을 시도하십시오. 그것은 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 
+0

블로그 게시물에 WITH (updlock, serializable) 힌트를 사용하여 결론을 내립니다. 그러나 MSDN을 읽는 중 상태는 "UPDLOCK - 트랜잭션이 완료 될 때까지 업데이트 잠금을 가져오고 유지하도록 지정합니다." 어쨌든 트랜잭션의 나머지 부분에 대해 업데이트 잠금이 유지되거나 뭔가 잘못 이해하게되면 직렬화 가능 힌트가 불필요한 것입니까? –

1

수정 디마 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을 통해 전송되는 데이터를 취하고 가능한 경우 내부적으로 수정하기 때문에이 작업이 필요했습니다.