Sıfırlanabilir kompozit endeks için yeniden birleştirme araması?


14

Aşağıdaki şema ve örnek veriler için

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 

Bir uygulama, bu tablodaki satırları 1000 satırlık kümeler halinde kümelenmiş dizin düzeninde işliyor.

İlk 1000 satır aşağıdaki sorgudan alınır.

SELECT TOP 1000 *
FROM   T
ORDER  BY A, B 

Bu setin son satırı aşağıda

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

Sadece bu bileşik dizin anahtarını arar ve sonra 1000 satır sonraki yığın almak için takip bir sorgu yazmak için herhangi bir yolu var mı?

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

Şimdiye kadar elde ettiğim en düşük okuma sayısı 1020'dir, ancak sorgu çok kıvrımlı görünüyor. Eşit veya daha iyi verimlilik için daha basit bir yol var mı? Belki de hepsini tek bir aralıkta yapmayı başarabilen biri?

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 

resim açıklamasını buraya girin


FWIW: Sütun Ayapılırsa NOT NULLve bir sentinel değeri -1kullanılırsa, bunun yerine eşdeğer yürütme planı kesinlikle daha basit görünür

resim açıklamasını buraya girin

Ancak plandaki tek arama operatörü hala tek bir bitişik aralığa daraltmak yerine iki arama gerçekleştirir ve mantıksal okumalar aynıdır, bu yüzden belki de bu kadar iyi olacağından şüpheleniyorum?


Benim hatam. NULLDeğerlerin her zaman ilk olduğunu unuttum . Düzeltilmiş koşulu (. aksini farz) Fiddle
ypercubeᵀᴹ

Evet, Oracle'ın farklı olduğuna inanıyorum.
Martin Smith


@ypercube - SQL Server maalesef bunun için düzenli bir tarama verir, bu nedenle uygulama tarafından zaten işlenmiş olan tüm satırları yeniden okur (mantıksal okuma 2015). İlk anahtarı aramıyor(NULL, 1000 )
Martin Smith

2 farklı koşulda, @Aboş olup olmamasına bağlı olarak, tarama yapmıyor gibi görünüyor. Ama planların sorgunuzdan daha iyi olup olmadığını anlayamıyorum. Fiddle-2
ypercubeᵀᴹ

Yanıtlar:


21

Sadece bu bileşik dizin anahtarını arar ve sonra 1000 satır sonraki yığın almak için takip bir sorgu yazmak için herhangi bir yolu var mı?

En sevdiğim bir çözüm APIimleç kullanmaktır :

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;

Genel strateji, çağrılar arasındaki konumunu hatırlayan tek bir taramadır . Bir APIimleç kullanmak, imleçte olduğu gibi bir seferde bir satır yerine bir satır bloğu döndürebileceğimiz anlamına gelir T-SQL:

Uygulama planları

STATISTICS IOÇıktısı:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.