개별 명세서 (DML, DDL 등)는 거래 자체입니다. 예, 루프가 반복 될 때마다 (기술적으로는 각 명령문 이후) 해당 UPDATE
명령문이 변경 되면 자동 커밋됩니다.
물론 예외는 항상 있습니다. 을 통해 암시 적 트랜잭션 수 있도록 할 수 있습니다 SET IMPLICIT_TRANSACTIONS 첫 번째 경우에, UPDATE
문은 당신이해야하는 트랜잭션을 시작할 것 COMMIT
또는 ROLLBACK
말을. 대부분의 경우 기본적으로 꺼져있는 세션 수준 설정입니다.
언제라도 취소 할 수 있도록 명시적인 BEGIN TRANSACTION / END TRANSACTION 문을 추가해야합니까?
실제로 프로세스를 중지했다가 다시 시작하려는 경우 프로세스를 중지하기 전에 프로세스를 중지 할 수 있으므로 명시 적 트랜잭션을 추가하거나 암시 적 트랜잭션을 활성화하는 것은 좋지 않습니다 COMMIT
. 이 경우 COMMIT
SSMS에있는 경우 수동으로 실행하거나 SQL 에이전트 작업에서이 작업을 실행하는 경우 해당 기회가 없어 고아 트랜잭션이 발생할 수 있습니다.
또한 @CHUNK_SIZE
더 작은 숫자 로 설정할 수 있습니다. 잠금 에스컬레이션은 일반적으로 단일 오브젝트에서 획득 한 5000 개의 잠금에서 발생합니다. 행 크기와 행 잠금 대 페이지 잠금을 수행하는 경우 해당 한계를 초과 할 수 있습니다. 행의 크기가 각 페이지 당 1 또는 2 개의 행만 들어가는 크기이면 페이지 잠금을 수행하더라도 항상이 오류가 발생할 수 있습니다.
테이블이 분할 된 경우 테이블을 에스컬레이션 할 때 전체 테이블이 아닌 파티션 만 잠그도록 LOCK_ESCALATION
테이블에 대한 옵션 (SQL Server 2008에 도입) 을 설정하는 옵션이 AUTO
있습니다. 또는 모든 테이블에 대해 동일한 옵션을로 설정할 수 DISABLE
있지만 매우주의해야합니다. 자세한 내용은 ALTER TABLE 을 참조하십시오.
다음은 잠금 에스컬레이션 및 임계 값에 대해 설명하는 문서입니다. 잠금 에스컬레이션 ( "SQL Server 2008 R2 이상 버전"에 적용됨). 다음은 잠금 에스컬레이션 감지 및 수정을 다루는 블로그 게시물입니다. Microsoft SQL Server에서 잠금 (12 부 – 잠금 에스컬레이션) .
정확한 질문과 관련이 없지만 질문의 쿼리와 관련하여 여기에서 수행 할 수있는 몇 가지 개선 사항이 있습니다 (적어도 그것을 보는 것만으로도 그렇게 보입니다).
WHILE (@@ROWCOUNT = @CHUNK_SIZE)
마지막 반복에서 업데이트 된 행 수가 UPDATE 요청 된 양보다 적 으면 수행 할 작업이 없으므로 루프의 경우 약간 더 좋습니다.
경우] deleted
필드는 인 BIT
데이터 유형은 다음의 여부에 의해 값을 결정하는 것이 아니다 deletedDate
이다 2000-01-01
? 왜 둘 다 필요합니까?
이 두 필드가 새 필드이고 NULL
온라인 / 비 차단 작업 일 수 있고 추가하여 "기본"값으로 업데이트하려는 경우에는 필요하지 않았습니다. SQL Server 2012 (Enterprise Edition 만 해당)부터 NOT NULL
DEFAULT 제약 조건이있는 열을 추가 하면 DEFAULT의 값이 상수 인 한 블로킹 작업이 아닙니다. 따라서 필드를 아직 사용하지 않는 경우 NOT NULL
DEFAULT 제약 조건을 사용하여 삭제하고 다시 추가하십시오 .
이 업데이트를 수행하는 동안 다른 프로세스가이 필드를 업데이트하지 않는 경우 업데이트하려는 레코드를 큐에 넣은 다음 해당 큐에서 작업하면 더 빠릅니다. 변경해야하는 세트를 얻기 위해 매번 테이블을 다시 쿼리해야하므로 현재 메소드에서 성능이 저하되었습니다. 대신 다음 두 가지 필드에서 한 번만 테이블을 스캔 한 다음 매우 목표가 지정된 UPDATE 문만 발행하는 다음을 수행 할 수 있습니다. 대기열의 초기 채우기는 단순히 업데이트 할 레코드를 찾기 때문에 언제든지 프로세스를 중지했다가 나중에 시작하면 위약금이 없습니다.
- 클러스터형 인덱스의 키 필드 만있는 임시 테이블 (#FullSet)을 만듭니다.
- 동일한 구조의 두 번째 임시 테이블 (#CurrentSet)을 만듭니다.
통해 #FullSet에 삽입 SELECT TOP(n) KeyField1, KeyField2 FROM [huge-table] where deleted is null or deletedDate is null;
이 TOP(n)
때문에 테이블의 크기에 거기에있다. 테이블에 1 억 개의 행이 있으면 특히 프로세스를 자주 중지하고 나중에 다시 시작하려는 경우 큐 테이블을 전체 키 세트로 채울 필요가 없습니다. 아마도 n
백만으로 설정 되어 완료 될 수 있습니다. 백만 세트 (또는 그 이하)를 실행하는 SQL 에이전트 작업에서 항상이를 예약 한 후 다음 예약 시간이 다시 걸릴 때까지 기다릴 수 있습니다. 그런 다음 20 분마다 실행하도록 스케줄을 설정하여 세트 사이에 강제 호흡 공간이 n
있지만 여전히 전체 프로세스를 무인으로 완료합니다. 그런 다음 더 이상 할 일이 없으면 작업을 삭제하십시오. :-).
- 루프에서 다음을 수행하십시오.
- 다음과 같은 것을 통해 현재 배치를 채 웁니다.
DELETE TOP (4995) FROM #FullSet OUTPUT Deleted.KeyField INTO #CurrentSet (KeyField);
IF (@@ROWCOUNT = 0) BREAK;
- 다음과 같은 것을 사용하여 업데이트를 수행하십시오.
UPDATE ht SET ht.deleted = 0, ht.deletedDate='2000-01-01' FROM [huge-table] ht INNER JOIN #CurrentSet cs ON cs.KeyField = ht.KeyField;
- 현재 세트를 지우십시오.
TRUNCATE TABLE #CurrentSet;
- 경우에 따라 필터링 된 인덱스를 추가
SELECT
하여 #FullSet
임시 테이블 로의 피드 를 지원하는 데 도움이 됩니다. 이러한 인덱스 추가와 관련된 몇 가지 고려 사항은 다음과 같습니다.
- WHERE 조건은 쿼리의 WHERE 조건과 일치해야합니다.
WHERE deleted is null or deletedDate is null
- 프로세스가 시작될 때 대부분의 행은 WHERE 조건과 일치하므로 인덱스가 도움이되지 않습니다. 이것을 추가하기 전에 50 % 정도가 될 때까지 기다릴 수 있습니다. 물론, 인덱스를 추가하는 것이 얼마나 도움이되고 언제 가장 좋은지 몇 가지 요인에 따라 달라 지므로 시행 착오가 조금 발생합니다.
- 기본 데이터가 자주 변경되기 때문에 인덱스를 수동으로 업데이트 및 / 또는 인덱스를 최신 상태로 유지해야 할 수도 있습니다.
- 을 돕는 동안 인덱스 는 해당 작업 중에 업데이트해야하는 또 다른 개체이므로 더 많은 I / O를 수행하므로 인덱스
SELECT
가 손상 될 수 있음을 명심하십시오 UPDATE
. 이것은 필터링 된 인덱스 (필터와 일치하는 행이 적기 때문에 행을 업데이트함에 따라 줄어 듭니다)와 인덱스를 추가하기 위해 잠시 기다립니다 (처음에 큰 도움이되지 않는 경우 발생하지 않을 이유가 없음) 추가 I / O).
업데이트 : 상태를 추적하고 깨끗하게 취소하는 메커니즘을 포함하여 위에서 제안 된 모든 기능을 구현하려면이 질문과 관련된 질문에 대한 답변을 참조하십시오 .SQL 서버 : 작은 덩어리로 거대한 테이블의 필드 업데이트 : 얻는 방법 진행 / 상태?