Bergabung kembali kisaran mencari indeks komposit nullable?


14

Untuk skema dan contoh data berikut

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 

Aplikasi sedang memproses baris dari tabel ini dalam urutan indeks berkerumun dalam 1.000 potongan baris.

1.000 baris pertama diambil dari kueri berikut.

SELECT TOP 1000 *
FROM   T
ORDER  BY A, B 

Baris terakhir dari set itu adalah di bawah ini

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

Apakah ada cara untuk menulis kueri yang hanya mencari kunci indeks komposit dan kemudian mengikutinya untuk mengambil potongan 1000 baris berikutnya?

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

Jumlah pembacaan terendah yang berhasil saya dapatkan sejauh ini adalah 1020 tetapi kueri tampaknya terlalu berbelit-belit. Apakah ada cara yang lebih sederhana untuk efisiensi yang sama atau lebih baik? Mungkin yang berhasil melakukan semuanya dalam satu rentang mencari?

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 

masukkan deskripsi gambar di sini


FWIW: Jika kolom Adibuat NOT NULLdan nilai sentinel -1digunakan sebagai gantinya, rencana eksekusi yang setara tentu terlihat lebih sederhana

masukkan deskripsi gambar di sini

Tetapi operator pencarian tunggal dalam rencana tersebut masih melakukan dua upaya daripada meruntuhkannya menjadi satu rentang yang berdekatan dan pembacaan logisnya hampir sama, jadi saya curiga bahwa mungkin ini cukup baik seperti yang akan didapat?


Kesalahanku. Saya lupa bahwa NULLnilai selalu yang pertama. (diasumsikan sebaliknya.) Kondisi terkoreksi di Fiddle
ypercubeᵀᴹ

Ya Oracle berbeda saya percaya.
Martin Smith


@ypercube - SQL Server hanya memberikan pemindaian yang terurut untuk sayangnya, jadi baca kembali semua baris yang sudah diproses oleh aplikasi (logical reads 2015). Itu tidak mencari ke kunci pertama(NULL, 1000 )
Martin Smith

Dengan 2 kondisi berbeda, apakah @Anull atau tidak, sepertinya tidak melakukan pemindaian. Tapi saya tidak bisa mengerti jika rencana lebih baik daripada permintaan Anda. Fiddle-2
ypercubeᵀᴹ

Jawaban:


21

Apakah ada cara untuk menulis kueri yang hanya mencari kunci indeks komposit dan kemudian mengikutinya untuk mengambil potongan 1000 baris berikutnya?

Solusi favorit saya adalah menggunakan APIkursor:

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;

Strategi keseluruhan adalah pemindaian tunggal yang mengingat posisinya di antara panggilan. Menggunakan APIkursor berarti kita dapat mengembalikan satu blok baris daripada satu demi satu seperti halnya T-SQLkursor:

Rencana eksekusi

The STATISTICS IOoutput:

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.