2010-02-17 1 views
8

SQL Server에 분당 수백 번 실행되는 SP가 있으며 데이터베이스에 대해 들어오는 트래픽을 확인해야합니다. 이 순간은 그러나INSERT 전후에 더 빠릅니다.

INSERT INTO table 
SELECT @value1,@value2 WHERE NOT EXISTS 
(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2); 

다음을 수행, 나 또한 더 빠른 것

IF NOT EXISTS(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2)  
    INSERT INTO table (value1,value2) VALUES (@value1,@value2); 

갈 수 있을까? 나는 그 (것)들 사이 다량 다름 아닙니다 그러나 나는 역사적으로 TSQL에 아주 좋지 않다 감각을 얻는다. =/

UPDATE : Whoops ...는 1 개 가치를 사용하여 레코드가 있으므로 고유 제한 조건이 작동하지 않습니다. ...

+0

http://stackoverflow.com/questions/2276023/t-sql-insert-or-update –

+0

@carlos : 그것은 다소 관련이 있지만 실제로는 다른 질문입니다. –

+1

두 번째 옵션은 안전하지 않습니다. 동시에'INSERT '는'IF'와'INSERT' 문 사이에서 발생할 수 있습니다. – Quassnoi

답변

1

이 질문과 답변에 대한 설명을 덧붙인 후에는 그에 대한 답변을 계속하겠습니다.

본래의 질문에서 제안 된 2 가지 사이의 성능에는 큰 차이가 없을 것으로 기대됩니다. 한편 레이 (Ray)가 지적한 것처럼, 두 번째 방법은 삽입에 대한 준비를하지 않을 수도 있지만, RDBMS는 일반적으로 첫 번째 솔루션 에서처럼 배치 문을 사용하여 가장 잘 수행됩니다.

KM과 DVK는 UNIQUE 제약 조건을 추가하는 것이 좋습니다. 이는 고유성 테스트를 암시 적으로 만들지 만 사용자의 INSERT 문에서 일종의 오류 처리를 추가해야합니다. 나는 이것이 추가 성능을 추가해야하는 이유를 알아 내기 힘들다. 인덱스를 이미 가지고 있다고 가정하고 . 그러한 색인이없는 경우 색인을 추가하고 성능 향상에 대한 필요성을 다시 생각하십시오.

고유성 검사가 명시 적으로 수행되는지 암시 적으로 수행되는지는 AFAIK와 관련이 없습니다.검사가 DBMS의 "내부"에서 수행되는 것으로 인해 얻게되는 것이 있으면 복제가 존재할 때 발생하는 오류를 처리하고 처리하는 오버 헤드에 의해 그 이득을 먹을 수도 있습니다.


결론은 : 당신은 여전히 ​​자신의 성능을 위해 lusting 발견하면 인덱스를 가정 내 포인트는 세 가지 솔루션을 제안에 당신이 경험적인 테스트를 수행한다는 것입니다, 이미 그 자리에있다. 예상되는 입력 데이터를 시뮬 레이팅하는 작은 프로그램을 작성하고 3 가지 솔루션을 각각 수십억 개의 행 (성공한 양의 중복 포함)으로 날려 버리십시오. 하는 거의 동시 환경에서

+0

감사합니다. 현재 각 열에는 색인이 있지만 둘 다 다루는 다중 열 색인은 없습니다. 나는 그것과 함께 가고 유일한 제약을 추가 할 것이라고 생각한다. 여러 열에 걸쳐 고유 한 제한 조건을 추가 할 수 있는지 알지 못했습니다. – roryok

+0

@roryok : 사실, 당신은 단지 당신의 인덱스를 '유일한 인덱스'로 변경할 수 있습니다, 그것은 동일 할 것입니다. – Quassnoi

+1

고마워! StackOverflow에 처음 게시하고 그것을 좋아합니다! – roryok

0

값을 고유하게하려면 값없이 고유 한 제약 조건을 만들고 SELECT없이 INSERT를 수행하고 제약 조건 위반 오류를 정상적으로 처리하는 것이 가장 좋은 이유는 무엇입니까?

이러한 방법보다 빠릅니다.

또한 첫 번째 접근 방식이 작동하지 않습니다. 선택하기 전까지 이미 값을 삽입 했으므로 select는 방금 삽입 한 내용을 분명히 찾습니다.

+2

나는 당신이 당신의 마지막 단락에서 틀렸다고 믿습니다. 'WHERE' 절은 실제로 삽입 이전에 실행되는'SELECT'에 바인드됩니다. –

+0

글쎄, 처음으로 StackOverflow 실수를 한 것 같다. 나는 그 예제를 단순화했다! 실제로 두 개의 값으로 레코드를 확인하므로 레코드가 고유하지 않습니다 ...이를 반영하여 편집합니다. 첫 번째 방법이 100 % 보장 될 수 있습니다. – roryok

+2

여러 열에 걸쳐있는 '고유 제한'을 추가해도 문제가되지 않습니다. –

0

내가 추측해야한다면, 두 번째 옵션이 빠를 것이라고 생각합니다. SQL Server는 존재하지 않는 경우 삽입에 대한 설정을 수행하지 않아도되지만 처음에는 테이블 및 필드 이름을 조회하여 절대로 발생하지 않는 삽입을 준비 할 수 있습니다. 그러나 쿼리 분석기에서이 쿼리를 시도하고 계획에서 말하는 내용을 확인합니다.

+0

반면에 첫 번째 옵션은 단일 배치 문이고 두 번째 옵션은 더 절차적인 스타일의 여러 문입니다. RDBMS는 일반적으로 배치 문과 함께 매우 효율적이지만 절차/명령형 코드에서는 그렇지 않습니다. 즉, 두 진술 중 어느 것이 가장 잘 수행되는지 모르겠다. –

1

BEGIN TRY 
    INSERT INTO Table (value) VALUES (@value); 
END TRY 
BEGIN CATCH 
    PRINT 'it was already in there!' 
END CATCH 

때문에이 분을 수백 번 실행 ... 그것을 할, 그리고 (값에 고유 제한 조건을 가정) 오류를 무시 잠금 힌트는 SELECT들에 추가되어야하며, avoid a race condition

(SELECT * FROM Table WITH (UPDLOCK, HOLDLOCK) WHERE value = @value); 

에 거래 그러나, 내 제안 된 아이디어는 INSERT뿐만 아니라 경쟁 조건을 피할 것이다 중복 제약 조건 오류를 무시합니다.

+0

감사합니다. KM, 내가 선택한 순간에 (NOLOCK)를 사용하고 있습니다 ... 언급 했어야합니다!두 통화가 동시에이 SQL을 변경하면 – roryok

+0

@roryok, 그들은 모두 기존 행을 선택 가능하게 할 수있는 동일한 가치와 생성하여 삽입하는 두 시도가 (당신은 시간 분의 수백을 실행하는 것을 말한다) 중복, 패배 귀하의 의도 –

+1

예외는 트리거 내부에서 발생하면 트랜잭션을 파기합니다. – Quassnoi

3

:-) 결과를 게시해야 이렇게 동시 INSERT는 두 번째 쿼리에 IF NOT EXISTSINSERT 사이에 발생할 수 있습니다.

첫 번째 쿼리는 검사 한 레코드에 공유 잠금을 배치합니다.이 공유 잠금은 쿼리가 끝날 때까지 해제되지 않으므로 쿼리가 실행될 때까지 새 레코드를 삽입 할 수 없습니다.

그러나이 동작에 전적으로 의존해서는 안됩니다. value에 추가 UNIQUE 제약 조건을 배치하십시오.

데이터베이스를 일관성있게 만들뿐만 아니라 첫 번째 쿼리를 더 빨리 만드는 인덱스를 만듭니다.

+2

+1 경쟁 조건의 위험을 언급합니다. –

5

두 변종이 모두 잘못되었습니다. 중복 @ 값 1, @ 값 2, 이 보장되는 쌍을 삽입합니다.

ALTER TABLE Table ADD CONSTRAINT uniqueValue1Value UNIQUE (value1, values2); 

및 삽입 :

BEGIN TRY 
    INSERT INTO Table (value1, value2) VALUES (@value1, @value2); 
END TRY 
BEGIN CATCH 
    DECLARE @error_number int, @error_message NVARCHAR(4000), @xact_state INT; 
    SET @error_number = ERROR_NUMBER(); 
    SET @error_message = ERROR_MESSAGE(); 
    SET @xact_state = XACT_STATE(); 
    IF (@xact_state = -1) 
    BEGIN 
    ROLLBACK TRANSACTION; 
    END 
    IF (@error_number != 2627) /* 2627 is ' Cannot insert duplicate key in object ...' */ 
    BEGIN 
     RAISERROR(N'Error inserting into Table: %i %s', 16,1, @errror_number, @error_message); 
    END 
ENd CATCH 

동안을

이 처리하는 올바른 방법은 열에 고유 제한 조건을 적용하고 항상 삽입하고 제약 조건 위반을 처리하는 것입니다 이것들은 복잡해 보일지도 모른다. 하나는 약간의 세부 사항을 가지고있다. 정확함은이다. 잠금 힌트 기반 솔루션과 비교할 때 훨씬 간단합니다. 이것은 또한 가장 효과적인 솔루션입니다. 오직 하나만 추구하십시오. 다른 모든 솔루션에는 적어도 두 개의 탐색이 필요합니다 (하나는 삽입 할 수 있는지 확인하고 하나는 삽입 할 수 있음).

+0

Remus에게 감사드립니다. ROLLBACK TRANSACTION이 영향을 미치는 거래는 무엇입니까? 값이 이미 켜져있는 경우 XACT_STATE는() 경우 전체 SP는이 삽입 문을 몇 가지 더 포함하는 트랜잭션 문에 동봉되어, 나는 ... 전체를 종료 – roryok

+1

을하지 않으려는 -1 당신은 선택의 여지가 , 그것은 transcation이 운명을 정한 것이고 * 반드시 rollback해야 함을 의미합니다. 유일한 제약 조건 위반은 롤백을 일으키지 않습니다. 예를 들어 디스크 공간이 부족한 runnig와 같이 더 심각한 오류가 발생한 경우에만 발생합니다. –

+0

토론에 3 년이 걸렸지 만 복제본이 보장된다는 성명서에 대해 궁금합니다. INSERT ... WHERE NOT EXISTS()가 중복을 일으킬 수있는 방법은 무엇입니까? INSERT & SELECT는 둘 다 동일한 트랜잭션에 있기 때문에 후자는 레코드를 잠그고 INSERT가 끝날 때까지 해제하지 않습니다. 이것은 또한 삽입이 완료 될 때까지 스스로 잠금을 얻을 수 없으므로 다른 연결이 동일한 INSERT를 만드는 것을 차단합니다. 나는 이것을 수년 동안 해왔고 결코 그것에 문제가 없었습니다. AFAIK는 다중 레코드 삽입 작업을 수행 할 때 사용할 수있는 유일한 유효한 접근 방식이기도합니다. – deroby