การเข้าร่วมช่วงใหม่ค้นหาดัชนีคอมโพสิตที่ไม่สามารถใช้ได้


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 |
+------+------+

มีวิธีใดในการเขียนคิวรีที่พยายามค้นหาคีย์ดัชนีคอมโพสิตนั้นจากนั้นติดตามมันเพื่อดึงข้อมูลอันถัดไปของแถว 1,000 แถว?

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

จำนวนการอ่านต่ำสุดที่ฉันได้รับจนถึงขณะนี้คือ 1,020 ข้อความค้นหาดูเหมือนซับซ้อนเกินไป มีวิธีที่ง่ายกว่าเท่ากันหรือมีประสิทธิภาพที่ดีขึ้น? บางทีคนที่จัดการเพื่อทำทุกอย่างในระยะเดียวแสวงหา?

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

ด้วย 2 เงื่อนไขที่แตกต่างกันไม่ว่าจะ@Aเป็นโมฆะหรือไม่ดูเหมือนว่าจะไม่ทำการสแกน แต่ฉันไม่เข้าใจว่าแผนดีกว่าแบบสอบถามของคุณหรือไม่ Fiddle-2
ypercubeᵀᴹ

คำตอบ:


21

มีวิธีใดในการเขียนคิวรีที่พยายามค้นหาคีย์ดัชนีคอมโพสิตนั้นจากนั้นติดตามมันเพื่อดึงข้อมูลอันถัดไปของแถว 1,000 แถว?

ทางออกที่โปรดปรานของฉันคือการใช้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.