SQL Server의 INSERT 또는 UPDATE 솔루션


598

의 테이블 구조를 가정합니다 MyTable(KEY, datafield1, datafield2...).

기존 레코드를 업데이트하거나 존재하지 않는 경우 새 레코드를 삽입하려고합니다.

본질적으로 :

IF (key exists)
  run update command
ELSE
  run insert command

이것을 작성하는 가장 좋은 방법은 무엇입니까?



27
이 질문을 처음 접하는 사람은 반드시 모든 답변과 의견을 읽으십시오. 나이는 때때로 잘못된 정보로 이어질 수 있습니다 ...
Aaron Bertrand

1
SQL Server 2005에 도입 된 EXCEPT 연산자를 사용해보십시오.
Tarzan

답변:


370

거래를 잊지 마십시오. 성능은 좋지만 단순 (IF EXISTS ..) 접근 방식은 매우 위험합니다.
여러 스레드가 삽입 또는 업데이트를 수행하려고 할 때 기본 키 위반을 쉽게 얻을 수 있습니다.

@Beau Crawford & @Esteban에서 제공하는 솔루션은 일반적인 아이디어를 보여 주지만 오류가 발생하기 쉽습니다.

교착 상태 및 PK 위반을 피하려면 다음과 같이 사용할 수 있습니다.

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert into table (key, ...)
   values (@key, ...)
end
commit tran

또는

begin tran
   update table with (serializable) set ...
   where key = @key

   if @@rowcount = 0
   begin
      insert into table (key, ...) values (@key,..)
   end
commit tran

질문은 가장 안전한 것이 아니라 가장 성능이 뛰어난 솔루션을 요구했습니다. 트랜잭션이 프로세스에 보안을 추가하는 동안 오버 헤드도 추가됩니다.
Luke Bennett

31
이 두 가지 방법 모두 여전히 실패 할 수 있습니다. 두 개의 동시 스레드가 동일한 행에서 동일하게 수행되면 첫 번째 스레드는 성공하지만 기본 키 위반으로 인해 두 번째 삽입에 실패합니다. 레코드가 존재하여 업데이트가 실패한 경우에도 트랜잭션이 삽입이 성공할 것이라고 보증하지 않습니다. 동시 트랜잭션 수에 관계없이 성공하려면 잠금을 사용해야합니다.
Jean Vincent

7
@aku BEGIN TRAN 직전에 "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"과 반대로 테이블 힌트 ( "with (xxxx)")를 사용한 이유는 무엇입니까?
EBarr

4
@CashCow, 마지막 승리, 이것은 INSERT 또는 UPDATE 가해 야 할 일입니다. 첫 번째 것은 삽입하고 두 번째는 레코드를 업데이트합니다. 잠금을 추가하면 매우 짧은 시간 내에이 오류가 발생하여 오류가 발생하지 않습니다.
Jean Vincent

1
나는 항상 잠금 힌트를 사용하는 것이 좋지 않다고 생각했으며 Microsoft 내부 엔진이 잠금을 지시하도록해야합니다. 이것이 규칙의 명백한 예외입니까?

381

매우 유사한 이전 질문에 대한 자세한 답변 보기

@Beau Crawford 's 는 SQL 2005 이하에서 좋은 방법이지만, 담당자에게 권한을 부여하려면 첫 번째 사람에게 가야 합니다 . 유일한 문제는 인서트의 경우 여전히 두 개의 IO 작업입니다.

MS Sql2008 merge은 SQL : 2003 표준에서 소개 합니다.

merge tablename with(HOLDLOCK) as target
using (values ('new value', 'different value'))
    as source (field1, field2)
    on target.idfield = 7
when matched then
    update
    set field1 = source.field1,
        field2 = source.field2,
        ...
when not matched then
    insert ( idfield, field1, field2, ... )
    values ( 7,  source.field1, source.field2, ... )

이제는 실제로 하나의 IO 작업이지만 끔찍한 코드 :-(


10
@Ian Boyd-예, 그것은 upsert다른 모든 DB 제공 업체가 대신 지원하기로 결정한 것이 아니라 SQL : 2003 표준의 구문 입니다. upsert는 T-SQL에서 유일하게 비 표준 키워드처럼 그렇지 않은 - 구문은 그래서 적어도 MS 너무 그것을 지원해야,이 작업을 수행 할 수있는 훨씬 더 좋은 방법입니다
키스

1
다른 답변의 잠금 힌트에 대한 의견이 있습니까? (곧 알게 될 것이지만 권장되는 방법이라면 답변에 추가하는 것이 좋습니다)
eglasius

25
구문을 사용할 때도 발생할 수있는 오류를 유발하는 경쟁 조건을 방지하는 방법에 대한 답변은 weblogs.sqlteam.com/dang/archive/2009/01/31/… 을 참조하십시오 MERGE.
Seph

5
@Seph 정말 놀랍습니다. Microsoft의 실패는 : -SI HOLDLOCK는 동시성이 높은 상황에서 병합 작업 이 필요하다는 것을 의미합니다 .
Keith

11
이 답변은 HOLDLOCK없이 스레드 안전하지 않다는 Seph의 의견을 설명하기 위해 실제로 업데이트되어야합니다. 링크 된 게시물에 따르면 MERGE는 암시 적으로 업데이트 잠금을 수행하지만 행을 삽입하기 전에 릴리스 잠금을 해제하므로 경쟁 조건 및 삽입시 기본 키 위반이 발생할 수 있습니다. HOLDLOCK을 사용하면 삽입이 발생할 때까지 잠금이 유지됩니다.
Triynko

169

UPSERT를 수행하십시오.

MyTable 설정 업데이트 FieldA = @ FieldA WHERE Key = @ Key

@@ ROWCOUNT = 0 인 경우
   MyTable에 삽입 (FieldA) 값 (@FieldA)

http://en.wikipedia.org/wiki/Upsert


7
적절한 고유 인덱스 제약 조건이 적용된 경우 기본 키 위반이 발생하지 않아야합니다. 제약 조건의 요점은 모든 중복 행이 발생하지 않도록하는 것입니다. 삽입하려는 스레드 수에 관계없이 데이터베이스는 제약 조건을 적용하는 데 필요한만큼 직렬화됩니다. 그렇지 않은 경우 엔진은 가치가 없습니다. 물론,이를 직렬화 된 트랜잭션에 래핑하면 교착 상태 또는 삽입 실패에 더 정확하고 덜 영향을받습니다.
Triynko

19
@Triynko, 나는 @Sam 사프란 두 개의 + 스레드가 올바른 순서로 인터리브 경우 다음 SQL Server는 것을 의미 생각 던져 기본 키 위반을 나타내는 오류는 것이다 발생했습니다. 직렬화 가능 트랜잭션으로 랩핑하는 것이 위의 명령문 세트에서 오류를 방지하는 올바른 방법입니다.
EBarr

1
자동 증분 인 기본 키가 있더라도 테이블에있을 수있는 고유 제한 조건이 문제가됩니다.
Seph

1
데이터베이스는 주요 키 문제를 처리해야합니다. 당신이 말하는 것은 업데이트가 실패하고 삽입으로 다른 프로세스가 먼저 도착하면 삽입이 실패한다는 것입니다. 이 경우 어쨌든 경쟁 조건이 있습니다. 잠금은 쓰기를 시도하는 프로세스 중 하나가 가치를 얻는다는 사후 조건이라는 사실을 변경하지 않습니다.
CashCow

93

많은 사람들이 당신에게 사용을 제안 할 MERGE것이지만, 나는 그것에 반대합니다. 기본적으로 여러 명령문 이상으로 동시성 및 경쟁 조건으로부터 사용자를 보호하지 않으며 다른 위험을 초래합니다.

http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/

이 "단순한"구문을 사용할 수 있어도 여전히이 접근 방식을 선호합니다 (간단하게하기 위해 오류 처리 생략).

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
  INSERT dbo.table(PK, ...) SELECT @PK, ...;
END
COMMIT TRANSACTION;

많은 사람들이 이런 식으로 제안합니다.

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS (SELECT 1 FROM dbo.table WHERE PK = @PK)
BEGIN
  UPDATE ...
END
ELSE
  INSERT ...
END
COMMIT TRANSACTION;

그러나이 모든 작업은 업데이트 할 행을 찾기 위해 테이블을 두 번 읽어야 할 수도 있습니다. 첫 번째 샘플에서는 행을 한 번만 찾으면됩니다. 두 경우 모두 초기 읽기에서 행을 찾지 못하면 삽입이 발생합니다.

다른 사람들은 이런 식으로 제안 할 것입니다 :

BEGIN TRY
  INSERT ...
END TRY
BEGIN CATCH
  IF ERROR_NUMBER() = 2627
    UPDATE ...
END CATCH

그러나 거의 모든 삽입이 실패하는 드문 시나리오를 제외하고 SQL Server에서 예외를 포착하도록 허용하는 것 외에 다른 이유가 없다면 훨씬 더 비쌉니다. 나는 여기에서 많은 것을 증명합니다.


3
많은 레코드를 삽입 / 업데이트하는 테이블에서 FROM 삽입 / 업데이트는 어떻습니까?
user960567

@ user960567 음,UPDATE target SET col = tmp.col FROM target INNER JOIN #tmp ON <key clause>; INSERT target(...) SELECT ... FROM #tmp AS t WHERE NOT EXISTS (SELECT 1 FROM target WHERE key = t.key);
Aaron Bertrand

4
좋은 2 년 이상 후 대답 :)
user960567

12
@ user960567 죄송합니다. 실시간으로 댓글 알림을 항상받는 것은 아닙니다.
Aaron Bertrand

60
IF EXISTS (SELECT * FROM [Table] WHERE ID = rowID)
UPDATE [Table] SET propertyOne = propOne, property2 . . .
ELSE
INSERT INTO [Table] (propOne, propTwo . . .)

편집하다:

아아, 심지어 내 자신의 손해에도 불구하고, 선택하지 않고이 작업을 수행하는 솔루션이 한 단계 적은 작업으로 작업을 수행하기 때문에 더 나은 것처럼 보입니다.


6
나는 여전히 이것을 좋아합니다. upsert는 부작용에 의한 프로그래밍과 비슷해 보였으며 실제 데이터베이스에서 성능 문제를 일으키는 초기 선택에 대한 클러스터링 인덱스 탐색이 거의 없었습니다 .
Eric Z Beard

38

한 번에 두 개 이상의 레코드를 UPSERT하려는 경우 ANSI SQL : 2003 DML 문 MERGE를 사용할 수 있습니다.

MERGE INTO table_name WITH (HOLDLOCK) USING table_name ON (condition)
WHEN MATCHED THEN UPDATE SET column1 = value1 [, column2 = value2 ...]
WHEN NOT MATCHED THEN INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...])

SQL Server 2005에서 MERGE 문 모방을 확인하십시오 .


1
오라클에서, 나는 MERGE 문 발행 생각하는 것은 테이블을 잠급니다. SQL * Server에서도 마찬가지입니까?
Mike McAllister

13
MERGE는 certian 잠금을 유지하지 않는 한 경쟁 조건 ( weblogs.sqlteam.com/dang/archive/2009/01/31/… 참조 )에 취약합니다 . 또한 SQL 프로파일 러에서 MERGE의 성능을 살펴보십시오 ... 보통 속도가 느리고 대체 솔루션보다 더 많은 읽기를 생성합니다.
EBarr

@EBarr-잠금 장치의 링크에 감사드립니다. 제안 잠금 힌트를 포함하도록 답변을 업데이트했습니다.
Eric Weilnau


10

이것에 대해 언급하기에는 늦었지만 MERGE를 사용하여보다 완전한 예제를 추가하고 싶습니다.

이러한 Insert + Update 문은 일반적으로 "Upsert"문이라고하며 SQL Server에서 MERGE를 사용하여 구현할 수 있습니다.

아주 좋은 예가 여기에 있습니다 : http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx

위는 잠금 및 동시성 시나리오도 설명합니다.

참고로 똑같이 인용 할 것입니다.

ALTER PROCEDURE dbo.Merge_Foo2
      @ID int
AS

SET NOCOUNT, XACT_ABORT ON;

MERGE dbo.Foo2 WITH (HOLDLOCK) AS f
USING (SELECT @ID AS ID) AS new_foo
      ON f.ID = new_foo.ID
WHEN MATCHED THEN
    UPDATE
            SET f.UpdateSpid = @@SPID,
            UpdateTime = SYSDATETIME()
WHEN NOT MATCHED THEN
    INSERT
      (
            ID,
            InsertSpid,
            InsertTime
      )
    VALUES
      (
            new_foo.ID,
            @@SPID,
            SYSDATETIME()
      );

RETURN @@ERROR;

1
MERGE와 걱정에 다른 일이 있습니다 mssqltips.com/sqlservertip/3074/...은
아론 버트 랜드

8
/*
CREATE TABLE ApplicationsDesSocietes (
   id                   INT IDENTITY(0,1)    NOT NULL,
   applicationId        INT                  NOT NULL,
   societeId            INT                  NOT NULL,
   suppression          BIT                  NULL,
   CONSTRAINT PK_APPLICATIONSDESSOCIETES PRIMARY KEY (id)
)
GO
--*/

DECLARE @applicationId INT = 81, @societeId INT = 43, @suppression BIT = 0

MERGE dbo.ApplicationsDesSocietes WITH (HOLDLOCK) AS target
--set the SOURCE table one row
USING (VALUES (@applicationId, @societeId, @suppression))
    AS source (applicationId, societeId, suppression)
    --here goes the ON join condition
    ON target.applicationId = source.applicationId and target.societeId = source.societeId
WHEN MATCHED THEN
    UPDATE
    --place your list of SET here
    SET target.suppression = source.suppression
WHEN NOT MATCHED THEN
    --insert a new line with the SOURCE table one row
    INSERT (applicationId, societeId, suppression)
    VALUES (source.applicationId, source.societeId, source.suppression);
GO

테이블 및 필드 이름을 원하는대로 바꾸십시오. 사용 ON 상태를 관리하십시오 . 그런 다음 DECLARE 행의 변수에 적절한 값 (및 유형)을 설정하십시오.

건배.


7

MERGE명령문 을 사용할 수 있습니다 .이 명령문은 존재하지 않는 경우 데이터를 삽입하거나 존재하는 경우 업데이트하는 데 사용됩니다.

MERGE INTO Employee AS e
using EmployeeUpdate AS eu
ON e.EmployeeID = eu.EmployeeID`

@RamenChef 이해가되지 않습니다. WHEN MATCHED 절은 어디에 있습니까?
likejudo

@likejudo 나는 이것을 쓰지 않았다. 나는 단지 그것을 수정했다. 게시물을 작성한 사용자에게 물어보십시오.
RamenChef

5

행이 업데이트되지 않은 경우 INSERT 경로로 업데이트하는 경우 경쟁 조건을 방지하기 위해 먼저 INSERT를 수행하는 것이 좋습니다 (중재 DELETE가 없다고 가정).

INSERT INTO MyTable (Key, FieldA)
   SELECT @Key, @FieldA
   WHERE NOT EXISTS
   (
       SELECT *
       FROM  MyTable
       WHERE Key = @Key
   )
IF @@ROWCOUNT = 0
BEGIN
   UPDATE MyTable
   SET FieldA=@FieldA
   WHERE Key=@Key
   IF @@ROWCOUNT = 0
   ... record was deleted, consider looping to re-run the INSERT, or RAISERROR ...
END

경쟁 조건을 피하는 것 외에도 대부분의 경우 레코드가 이미 존재하면 INSERT가 실패하여 CPU가 낭비됩니다.

SQL2008 이후에는 MERGE를 사용하는 것이 좋습니다.


흥미로운 아이디어이지만 잘못된 구문입니다. SELECT에는 FROM <table_source>와 TOP 1이 필요합니다 (선택된 table_source에 행이 하나만있는 경우 제외).
jk7

감사. 존재하지 않음으로 변경했습니다. O / P에 따라 "키"에 대한 테스트 때문에 일치하는 행이 하나만있을 것입니다 (다중 부분 키가 필요할 수도 있음).
Kristen

4

사용 패턴에 따라 다릅니다. 세부 사항에서 길을 잃지 않고 사용 큰 그림을 봐야합니다. 예를 들어, 레코드가 작성된 후 사용 패턴이 99 % 업데이트 인 경우 'UPSERT'가 최상의 솔루션입니다.

첫 번째 삽입 (적중) 후에는 모든 단일 명령문 업데이트가되며 if 또는 buts는 없습니다. 인서트의 'where'조건이 필요합니다. 그렇지 않으면 복제본이 삽입되므로 잠금을 처리하고 싶지 않습니다.

UPDATE <tableName> SET <field>=@field WHERE key=@key;

IF @@ROWCOUNT = 0
BEGIN
   INSERT INTO <tableName> (field)
   SELECT @field
   WHERE NOT EXISTS (select * from tableName where key = @key);
END

2

MS SQL Server 2008에는 MERGE 문이 도입되었으며 이는 SQL : 2003 표준의 일부라고 생각합니다. 많은 사람들이 보여 주듯이 한 행 사례를 처리하는 것은 큰 문제가 아니지만 큰 데이터 세트를 처리 할 때 발생하는 모든 성능 문제와 함께 커서가 필요합니다. MERGE 문은 큰 데이터 세트를 처리 할 때 추가로 환영받을 것입니다.


1
커서를 사용하여 큰 데이터 세트 로이 작업을 수행 할 필요가 없었습니다. 일치하는 레코드를 업데이트하고 테이블에 조인 한 값 절 대신 select를 사용하여 삽입을 업데이트하는 업데이트 만 있으면됩니다.
HLGEM

1

모든 사람들이 sprocs를 직접 실행하는 악의적 인 사용자로부터 두려움으로부터 HOLDLOCK-s로 뛰어 들기 전에 :-) 디자인 (신분 키, Oracle의 시퀀스 생성기, 고유 인덱스)에 의해 새로운 PK의 고유성을 보장해야 한다고 지적했습니다. 외부 ID, 색인으로 덮인 쿼리). 이것이 문제의 알파와 오메가입니다. 당신이 그것을 가지고 있지 않다면, 우주의 HOLDLOCK-s가 당신을 구하지 않을 것이고 당신이 그것을 가지고 있다면 당신은 첫 번째 선택에서 UPDLOCK 이외의 것을 필요로하지 않습니다 (또는 먼저 업데이트를 사용하기 위해).

Sprocs는 일반적으로 매우 제어 된 조건 하에서 신뢰할 수있는 호출자 (미드 티어)를 가정하여 실행됩니다. 즉, 간단한 upsert 패턴 (update + insert 또는 merge)이 중복 PK를 발견하여 미드 티어 또는 테이블 디자인의 버그를 의미하는 경우 SQL은 이러한 경우 오류를 알리고 레코드를 거부하는 것이 좋습니다. 이 경우 HOLDLOCK을 배치하면 성능 저하 외에 예외를 먹고 결함이있는 데이터를 가져 오는 것과 같습니다.

MERGE 또는 UPDATE를 사용하면 서버에서 INSERT가 더 쉬우 며 먼저 선택할 때 (UPDLOCK)을 추가 할 필요가 없으므로 오류가 덜 발생합니다. 또한 작은 배치로 삽입 / 업데이트를 수행하는 경우 트랜잭션이 적절한 지 여부를 결정하기 위해 데이터를 알아야합니다. 그것은 단지 관련이없는 레코드의 모음 일뿐입니다. 그러면 추가적인 "봉투"트랜잭션은 해로울 것입니다.


1
업데이트를 수행 한 다음 잠금 또는 격리하지 않고 삽입하면 두 명의 사용자가 동일한 데이터를 다시 전달하려고 시도 할 수 있습니다 (두 명의 사용자가 동일한 정보를 제출하려고하면 중간 계층의 버그로 간주하지 않습니다) 같은 시간-상황에 따라 크게 다르지 않습니까?). 둘 다 업데이트에 들어가서 둘 다 0 행을 반환 한 다음 둘 다 삽입하려고합니다. 하나는 이기고 다른 하나는 예외를 얻습니다. 사람들이 보통 피하려고하는 것입니다.
Aaron Bertrand

1

업데이트 후 삽입을 시도하면 경쟁 조건이 실제로 중요합니까? 키 의 값을 설정하려는 두 개의 스레드가 있다고 가정 해 보겠습니다 .

스레드 1 : 값 = 1
스레드 2 : 값 = 2

경쟁 조건 시나리오 예

  1. 가 정의되지 않았습니다
  2. 스레드 1이 업데이트 실패
  3. 스레드 2가 업데이트 실패
  4. 스레드 1 또는 스레드 2 중 정확히 하나가 삽입에 성공합니다. 예를 들어 실 1
  5. 다른 스레드는 삽입 (오류 중복 키)-스레드 2로 실패합니다.

    • 결과 : 삽입 할 두 트레드의 "첫 번째"값을 결정합니다.
    • 원하는 결과 : 데이터를 기록 (업데이트 또는 삽입) 할 마지막 2 개의 스레드가 값을 결정해야합니다.

그러나; 다중 스레드 환경에서 OS 스케줄러는 스레드 실행 순서를 결정합니다. 위의 시나리오에서이 경쟁 조건이있는 경우에는 실행 순서를 결정한 OS였습니다. 즉 : "스레드 1"또는 "스레드 2"가 시스템 관점에서 "먼저"라고 말하는 것은 잘못입니다.

스레드 1과 스레드 2에 대해 실행 시간이 너무 가까운 경우 경쟁 조건의 결과는 중요하지 않습니다. 유일한 요구 사항은 스레드 중 하나가 결과 값을 정의해야한다는 것입니다.

구현의 경우 : update 다음에 insert로 인해 "중복 키"오류가 발생하면 성공으로 간주해야합니다.

또한 데이터베이스의 값이 마지막으로 쓴 값과 동일하다고 가정해서는 안됩니다.


1

SQL Server 2008에서는 MERGE 문을 사용할 수 있습니다


11
이것은 의견입니다. 실제 예제 코드가 없으면 사이트에 대한 다른 많은 의견과 같습니다.
swasheck

아주 오래되었지만 예제가 좋을 것입니다.
Matt McCabe

0

나는 아래 해결책을 시도했고 insert 문에 대한 동시 요청이 발생할 때 나를 위해 일한다.

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert table (key, ...)
   values (@key, ...)
end
commit tran

0

이 쿼리를 사용할 수 있습니다. 모든 SQL Server 버전에서 작동합니다. 간단하고 명확합니다. 그러나 두 개의 쿼리를 사용해야합니다. MERGE를 사용할 수없는 경우 사용할 수 있습니다

    BEGIN TRAN

    UPDATE table
    SET Id = @ID, Description = @Description
    WHERE Id = @Id

    INSERT INTO table(Id, Description)
    SELECT @Id, @Description
    WHERE NOT EXISTS (SELECT NULL FROM table WHERE Id = @Id)

    COMMIT TRAN

참고 : 답변 부정적인 것을 설명하십시오


잠금이 부족한 것 같아요?
Zeek2

잠금 부족이 없습니다 ... "TRAN"을 사용합니다. 기본 SQL Server 트랜잭션은 잠금 상태입니다.
빅터 산체스

-2

ADO.NET을 사용하면 DataAdapter가이를 처리합니다.

직접 처리하려면 다음과 같이하십시오.

키 열에 기본 키 제약 조건이 있는지 확인하십시오.

그럼 당신은 :

  1. 업데이트를
  2. 키가있는 레코드가 이미 존재하여 업데이트에 실패하면 삽입을 수행하십시오. 업데이트가 실패하지 않으면 완료된 것입니다.

다른 방법으로도 할 수 있습니다. 즉, 먼저 삽입을 수행하고 삽입이 실패하면 업데이트를 수행하십시오. 일반적으로 첫 번째 방법은 삽입보다 업데이트가 더 자주 수행되므로 더 좋습니다.


... 삽입을 먼저 수행하면 (때로는 실패한다는 것을 알고 있음) SQL Server에 비용이 많이 듭니다. sqlperformance.com/2012/08/t-sql-queries/error-handling
Aaron Bertrand

-3

if 존재하는 경우 ... else ...는 최소 두 개의 요청 (하나는 확인하고 하나는 조치를 취)을 수행합니다. 다음 접근법은 레코드가 존재하는 곳에 하나만 필요하며, 삽입이 필요한 경우에는 두 가지가 필요합니다.

DECLARE @RowExists bit
SET @RowExists = 0
UPDATE MyTable SET DataField1 = 'xxx', @RowExists = 1 WHERE Key = 123
IF @RowExists = 0
  INSERT INTO MyTable (Key, DataField1) VALUES (123, 'xxx')

-3

나는 보통 기존의 포스터를 먼저 확인하고 올바른 경로가 무엇이든간에 다른 포스터 중 몇 가지가 말한 것을 수행합니다. 이 작업을 수행 할 때 기억해야 할 것은 sql에 의해 캐시 된 실행 계획이 한 경로 나 다른 경로에 대해 최적이 아닐 수 있다는 것입니다. 가장 좋은 방법은 두 가지 저장 프로 시저를 호출하는 것입니다.

FirstSP :
존재하는 경우
   SecondSP 호출 (UpdateProc)
그밖에
   ThirdSP 호출 (InsertProc)

지금, 나는 종종 내 자신의 조언을 따르지 않기 때문에 소금 한알로 가져 가십시오.


이것은 고대 버전의 SQL Server와 관련이 있었지만 최신 버전에는 문 수준 컴파일이 있습니다. 포크 등은 문제가되지 않으며, 이런 것들에 대해 별도의 절차를 사용한다고해서 업데이트와 삽입 중에서 선택하는 데 내재 된 문제는 해결되지 않습니다 ...
Aaron Bertrand

-10

선택하면 결과가 나오면 업데이트하고 그렇지 않은 경우 업데이트하십시오.


3
데이터베이스에 대한 두 번의 호출입니다.
Chris Cudmore

3
나는 그것에 문제가 보이지 않습니다.
클린트 에커

10
문제는 DB에 대한 두 번의 호출이므로 DB에 대한 왕복 횟수를 두 배로 늘리는 것입니다. 앱이 많은 삽입 / 업데이트로 데이터베이스에 충돌하면 성능이 저하됩니다. UPSERT가 더 나은 전략입니다.
Kev

5
또한 경쟁 조건을 만들지 않습니까?
niico
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.