재결합 범위는 nullable 복합 인덱스를 찾고 있습니까?


14

다음 스키마 및 예제 데이터

CREATE TABLE T
  (
     A INT NULL,
     B INT NOT NULL IDENTITY,
     C CHAR(8000) NULL,
     UNIQUE CLUSTERED (A, B)
  )

INSERT INTO T
            (A)
SELECT NULLIF(( ( ROW_NUMBER() OVER (ORDER BY @@SPID) - 1 ) / 1003 ), 0)
FROM   master..spt_values 

애플리케이션이이 테이블의 행을 1,000 행 청크로 클러스터 된 인덱스 순서로 처리 중입니다.

다음 쿼리에서 처음 1,000 개의 행이 검색됩니다.

SELECT TOP 1000 *
FROM   T
ORDER  BY A, B 

해당 세트의 마지막 행은 다음과 같습니다

+------+------+
|  A   |  B   |
+------+------+
| NULL | 1000 |
+------+------+

복합 인덱스 키를 찾은 다음 1000 행의 다음 청크를 검색하기 위해 쿼리를 작성하는 방법이 있습니까?

/*Pseudo Syntax*/
SELECT TOP 1000 *
FROM   T
WHERE (A, B) is_ordered_after (@A, @B)
ORDER  BY A, B 

지금까지 내가 읽은 가장 적은 읽기 수는 1020이지만 쿼리가 너무 복잡합니다. 동일하거나 더 효율적인 효율성의 방법이 있습니까? 아마도 한 범위에서 모든 것을 수행 할 수있는 사람이 있습니까?

DECLARE @A INT = NULL, @B INT = 1000

;WITH UnProcessed
     AS (SELECT *
         FROM   T
         WHERE  ( EXISTS(SELECT A
                         INTERSECT
                         SELECT @A)
                  AND B > @B )
         UNION ALL
         SELECT *
         FROM   T
         WHERE @A IS NULL AND A IS NOT NULL
         UNION ALL
         SELECT *
         FROM   T
         WHERE A > @A        
         )
SELECT TOP 1000 *
FROM   UnProcessed
ORDER  BY A,
          B 

여기에 이미지 설명을 입력하십시오


FWIW : 열 A을 만들고 NOT NULL센티넬 값을 -1대신 사용하면 동등한 실행 계획이 확실히 단순 해 보입니다.

여기에 이미지 설명을 입력하십시오

그러나 계획의 단일 탐색 연산자 는 단일 연속 범위로 축소하지 않고 여전히 두 가지 탐색을 수행 하며 논리적 읽기는 거의 동일하므로 아마도 이것이 얻을 수있는만큼 좋을 것으로 의심됩니다.


내 실수. 나는 NULL항상 값이 첫 번째 라는 것을 잊었다 . Fiddle
ypercubeᵀᴹ

예, 오라클은 다릅니다.
Martin Smith


@ypercube-SQL Server는 불행히도 순서대로 스캔을 수행하므로 응용 프로그램에서 이미 처리 한 모든 행을 다시 읽습니다 (논리적 읽기 2015). 그것은 첫 번째 열쇠를 찾지 않습니다(NULL, 1000 )
Martin Smith

@Anull 여부에 따라 두 가지 조건 이 있으면 스캔을 수행하지 않는 것 같습니다. 그러나 계획이 쿼리보다 낫다면 이해할 수 없습니다. 바이올린 2
ypercubeᵀᴹ

답변:


21

복합 인덱스 키를 찾은 다음 1000 행의 다음 청크를 검색하기 위해 쿼리를 작성하는 방법이 있습니까?

가장 좋아하는 솔루션은 API커서 를 사용하는 것입니다 .

SET NOCOUNT ON;
SET STATISTICS IO ON;

DECLARE 
    @cur integer,
    -- FAST_FORWARD, AUTO_FETCH, AUTO_CLOSE, CHECK_ACCEPTED_TYPES, FAST_FORWARD_ACCEPTABLE
    @scrollopt integer = 16 | 8192 | 16384 | 32768 | 1048576,
    -- READ_ONLY, CHECK_ACCEPTED_OPTS, READ_ONLY_ACCEPTABLE
    @ccopt integer = 1 | 32768 | 65536, 
    @rowcount integer = 1000,
    @rc integer;

-- Open the cursor and return (up to) the first 1000 rows
EXECUTE @rc = sys.sp_cursoropen
    @cur OUTPUT,
    N'
    SELECT A, B, C
    FROM T
    ORDER BY A, B;
    ',
    @scrollopt OUTPUT,
    @ccopt OUTPUT,
    @rowcount OUTPUT;

IF @rc <> 16 -- FastForward cursor automatically closed
BEGIN
    -- Name the cursor so we can use CURSOR_STATUS
    EXECUTE sys.sp_cursoroption
        @cur, 
        2, 
        'MyCursorName';

    -- Until the cursor auto-closes
    WHILE CURSOR_STATUS('global', 'MyCursorName') = 1
    BEGIN
        EXECUTE sys.sp_cursorfetch
            @cur,
            2,
            0,
            1000;
    END;
END;

SET STATISTICS IO OFF;

전체 전략은 통화 간 위치기억 하는 단일 스캔입니다 . API커서를 사용한다는 것은 커서의 경우와 같이 한 번에 하나씩이 아니라 행 블록을 반환 할 수 있음을 의미합니다 T-SQL.

실행 계획

STATISTICS IO출력은 다음과 같습니다

Table 'T'. Scan count 1, logical reads 1011, physical reads 0, read-ahead reads 0
Table 'T'. Scan count 1, logical reads 1001, physical reads 0, read-ahead reads 0
Table 'T'. Scan count 1, logical reads 516, physical reads 0, read-ahead reads 0
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.