ปรับช่วงตัวเลข (ช่วงเวลา) ให้เหมาะสมที่สุดใน SQL Server


18

คำถามนี้คล้ายกับการเพิ่มประสิทธิภาพการค้นหาช่วง IP หรือไม่ แต่อันนั้นถูก จำกัด ไว้ที่ SQL Server 2000

สมมติว่าฉันมี 10 ล้านช่วงที่จัดเก็บไว้ชั่วคราวในตารางที่มีโครงสร้างและมีประชากรดังนี้

CREATE TABLE MyTable
(
Id        INT IDENTITY PRIMARY KEY,
RangeFrom INT NOT NULL,
RangeTo   INT NOT NULL,
CHECK (RangeTo > RangeFrom),
INDEX IX1 (RangeFrom,RangeTo),
INDEX IX2 (RangeTo,RangeFrom)
);

WITH RandomNumbers
     AS (SELECT TOP 10000000 ABS(CRYPT_GEN_RANDOM(4)%100000000) AS Num
         FROM   sys.all_objects o1,
                sys.all_objects o2,
                sys.all_objects o3,
                sys.all_objects o4)
INSERT INTO MyTable
            (RangeFrom,
             RangeTo)
SELECT Num,
       Num + 1 + CRYPT_GEN_RANDOM(1)
FROM   RandomNumbers 

50,000,000ฉันจำเป็นต้องรู้ทุกช่วงที่มีค่า ฉันลองใช้แบบสอบถามต่อไปนี้

SELECT *
FROM MyTable
WHERE 50000000 BETWEEN RangeFrom AND RangeTo

SQL Server แสดงว่ามีการอ่านแบบลอจิคัล 10,951 ครั้งและอ่านได้เกือบ 5 ล้านแถวเพื่อส่งคืนการจับคู่ 12 รายการ

ป้อนคำอธิบายรูปภาพที่นี่

ฉันสามารถปรับปรุงประสิทธิภาพนี้ได้ไหม การปรับโครงสร้างของตารางหรือดัชนีเพิ่มเติมใด ๆ นั้นเป็นเรื่องปกติ


หากฉันเข้าใจการตั้งค่าของตารางอย่างถูกต้องคุณกำลังเลือกตัวเลขสุ่มอย่างสม่ำเสมอเพื่อสร้างช่วงของคุณโดยไม่มีข้อ จำกัด ใน "ขนาด" ของแต่ละช่วง และโพรบของคุณนั้นอยู่ตรงกลางของช่วงโดยรวม 1..100M ในกรณีนี้ - ไม่มีการจัดกลุ่มที่ชัดเจนเนื่องจากการสุ่มแบบสม่ำเสมอ - ฉันไม่รู้ว่าทำไมดัชนีในขอบเขตล่างหรือบนจะเป็นประโยชน์ คุณอธิบายได้ไหม
davidbak

@davidbak ดัชนีทั่วไปในตารางนี้ไม่ได้มีประโยชน์มากในกรณีที่เลวร้ายที่สุดเพราะมันต้องสแกนครึ่งช่วงจึงขอการปรับปรุงที่อาจเกิดขึ้น มีการปรับปรุงที่ดีในคำถามที่เชื่อมโยงสำหรับ SQL Server 2000 ด้วยการแนะนำ "แกรนูล" ฉันหวังว่าดัชนีเชิงพื้นที่จะช่วยได้ที่นี่เนื่องจากพวกเขาสนับสนุนการcontainsสืบค้นและในขณะที่พวกเขาทำงานได้ดีในการลดปริมาณข้อมูลที่อ่าน ค่าใช้จ่ายที่ต่อต้านสิ่งนี้
Martin Smith

ฉันไม่มีเครื่องมืออำนวยความสะดวกให้ลอง - แต่ฉันสงสัยว่าสองดัชนีหรือไม่ - หนึ่งที่ขอบเขตล่างหนึ่งที่ด้านบน - และจากนั้นเข้าร่วมภายใน - จะช่วยให้แบบสอบถามเพิ่มประสิทธิภาพการทำงานบางอย่างออก
davidbak

คำตอบ:


11

Columnstore นั้นดูดีมากเมื่อเทียบกับดัชนีแบบ nonclustered ซึ่งจะสแกนครึ่งตาราง ดัชนี columnstore ที่ไม่ใช่คลัสเตอร์ให้ประโยชน์มากที่สุด แต่การแทรกข้อมูลที่สั่งซื้อลงในดัชนี columnstore แบบคลัสเตอร์จะดียิ่งขึ้น

DROP TABLE IF EXISTS dbo.MyTableCCI;

CREATE TABLE dbo.MyTableCCI
(
Id        INT PRIMARY KEY,
RangeFrom INT NOT NULL,
RangeTo   INT NOT NULL,
CHECK (RangeTo > RangeFrom),
INDEX CCI CLUSTERED COLUMNSTORE
);

INSERT INTO dbo.MyTableCCI
SELECT TOP (987654321) *
FROM dbo.MyTable
ORDER BY RangeFrom ASC
OPTION (MAXDOP 1);

จากการออกแบบฉันจะได้รับการกำจัดแถวกลุ่มในRangeFromคอลัมน์ซึ่งจะกำจัดครึ่งหนึ่งของกลุ่มแถวของฉัน แต่เนื่องจากลักษณะของข้อมูลฉันยังได้รับการกำจัดแถวกลุ่มในRangeToคอลัมน์เช่นกัน:

Table 'MyTableCCI'. Segment reads 1, segment skipped 9.

สำหรับตารางขนาดใหญ่ที่มีข้อมูลตัวแปรมากขึ้นมีวิธีที่แตกต่างกันในการโหลดข้อมูลเพื่อรับประกันการกำจัดแถวกลุ่มที่ดีที่สุดในทั้งสองคอลัมน์ โดยเฉพาะอย่างยิ่งข้อมูลของคุณแบบสอบถามใช้เวลา 1 มิลลิวินาที


แน่นอนว่ากำลังมองหาวิธีการอื่นในการพิจารณาโดยไม่มีข้อ จำกัด ในปี 2000 เสียงไม่เหมือนที่จะถูกตี
Martin Smith

9

Paul White ชี้ให้เห็นคำตอบของคำถามที่คล้ายกันซึ่งมีลิงก์ไปยังบทความที่น่าสนใจโดย Itzik Ben Ganเบนกาน สิ่งนี้อธิบายถึงโมเดล "ทรีช่วงเวลาแบบสแตติกสัมพันธ์" ที่อนุญาตให้ทำสิ่งนี้ได้อย่างมีประสิทธิภาพ

โดยสรุปวิธีการนี้เกี่ยวข้องกับการจัดเก็บค่าที่คำนวณ ("forknode") ตามค่าช่วงเวลาในแถว เมื่อค้นหาช่วงที่ตัดกันอีกช่วงหนึ่งเป็นไปได้ที่จะคำนวณค่า forknode ที่เป็นไปได้ที่แถวที่ตรงกันต้องมีและใช้สิ่งนี้เพื่อค้นหาผลลัพธ์ที่มีการดำเนินการค้นหาสูงสุด 31 รายการ (ด้านล่างรองรับจำนวนเต็มในช่วง 0 ถึงสูงสุดที่ลงนาม 32 บิต int)

จากนี้ฉันปรับโครงสร้างตารางด้านล่าง

CREATE TABLE dbo.MyTable3
(
  Id        INT IDENTITY PRIMARY KEY,
  RangeFrom INT NOT NULL,
  RangeTo   INT NOT NULL,   
  node  AS RangeTo - RangeTo % POWER(2, FLOOR(LOG((RangeFrom - 1) ^ RangeTo, 2))) PERSISTED NOT NULL,
  CHECK (RangeTo > RangeFrom)
);

CREATE INDEX ix1 ON dbo.MyTable3 (node, RangeFrom) INCLUDE (RangeTo);
CREATE INDEX ix2 ON dbo.MyTable3 (node, RangeTo) INCLUDE (RangeFrom);

SET IDENTITY_INSERT MyTable3 ON

INSERT INTO MyTable3
            (Id,
             RangeFrom,
             RangeTo)
SELECT Id,
       RangeFrom,
       RangeTo
FROM   MyTable

SET IDENTITY_INSERT MyTable3 OFF 

และจากนั้นใช้แบบสอบถามต่อไปนี้ (บทความกำลังมองหาช่วงเวลาที่ตัดกันดังนั้นการค้นหาช่วงเวลาที่มีจุดเป็นกรณีที่เลวลงของสิ่งนี้)

DECLARE @value INT = 50000000;

;WITH N AS
(
SELECT 30 AS Level, 
       CASE WHEN @value > POWER(2,30) THEN POWER(2,30) END AS selected_left_node, 
       CASE WHEN @value < POWER(2,30) THEN POWER(2,30) END AS selected_right_node, 
       (SIGN(@value - POWER(2,30)) * POWER(2,29)) + POWER(2,30)  AS node
UNION ALL
SELECT N.Level-1,   
       CASE WHEN @value > node THEN node END AS selected_left_node,  
       CASE WHEN @value < node THEN node END AS selected_right_node,
       (SIGN(@value - node) * POWER(2,N.Level-2)) + node  AS node
FROM N 
WHERE N.Level > 0
)
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
  JOIN N AS L
    ON I.node = L.selected_left_node
    AND I.RangeTo >= @value
    AND L.selected_left_node IS NOT NULL
UNION ALL
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
  JOIN N AS R
    ON I.node = R.selected_right_node
    AND I.RangeFrom <= @value
    AND R.selected_right_node IS NOT NULL
UNION ALL
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
WHERE node = @value;

โดยทั่วไปจะดำเนินการใน1msเครื่องของฉันเมื่อทุกหน้าอยู่ในแคช - ด้วยสถิติ IO

Table 'MyTable3'. Scan count 24, logical reads 72, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 4, logical reads 374, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

และวางแผน

ป้อนคำอธิบายรูปภาพที่นี่

หมายเหตุ: แหล่งที่มาใช้ TVF แบบหลายขั้นตอนมากกว่า CTE แบบเรียกซ้ำเพื่อให้ได้โหนดเพื่อเข้าร่วม แต่เพื่อประโยชน์ในการทำให้คำตอบของตัวเองมีอยู่ฉันได้เลือกใช้หลัง สำหรับการใช้งานจริงฉันควรใช้ TVFs


9

ฉันสามารถค้นหาวิธีการแบบแถวที่แข่งขันได้กับวิธี N / CCI แต่คุณต้องรู้อะไรเกี่ยวกับข้อมูลของคุณ สมมติว่าคุณมีคอลัมน์ที่มีความแตกต่างRangeFromและRangeToและคุณได้จัดทำดัชนีไว้พร้อมกับRangeFrom:

ALTER TABLE dbo.MyTableWithDiff ADD DiffOfColumns AS RangeTo-RangeFrom;

CREATE INDEX IXDIFF ON dbo.MyTableWithDiff (DiffOfColumns,RangeFrom) INCLUDE (RangeTo);

หากคุณทราบค่าที่แตกต่างกันทั้งหมดDiffOfColumnsคุณสามารถทำการค้นหาค่าทุกค่าDiffOfColumnsด้วยตัวกรองพิสัยRangeToเพื่อรับข้อมูลที่เกี่ยวข้องทั้งหมด ตัวอย่างเช่นถ้าเรารู้ว่าDiffOfColumns = 2 ค่าที่อนุญาตเพียงอย่างเดียวRangeFromคือ 49999998, 49999999 และ 50000000 การเรียกซ้ำสามารถใช้เพื่อให้ได้ค่าที่แตกต่างทั้งหมดDiffOfColumnsและทำงานได้ดีสำหรับชุดข้อมูลของคุณเนื่องจากมีเพียง 256 ค่า แบบสอบถามด้านล่างใช้เวลาประมาณ 6 ms ในเครื่องของฉัน:

WITH RecursiveCTE
AS
(
    -- Anchor
    SELECT TOP (1)
        DiffOfColumns
    FROM dbo.MyTableWithDiff AS T
    ORDER BY
        T.DiffOfColumns

    UNION ALL

    -- Recursive
    SELECT R.DiffOfColumns
    FROM
    (
        -- Number the rows
        SELECT 
            T.DiffOfColumns,
            rn = ROW_NUMBER() OVER (
                ORDER BY T.DiffOfColumns)
        FROM dbo.MyTableWithDiff AS T
        JOIN RecursiveCTE AS R
            ON R.DiffOfColumns < T.DiffOfColumns
    ) AS R
    WHERE
        -- Only the row that sorts lowest
        R.rn = 1
)
SELECT ca.*
FROM RecursiveCTE rcte
CROSS APPLY (
    SELECT mt.Id, mt.RangeFrom, mt.RangeTo
    FROM dbo.MyTableWithDiff mt
    WHERE mt.DiffOfColumns = rcte.DiffOfColumns
    AND mt.RangeFrom >= 50000000 - rcte.DiffOfColumns AND mt.RangeFrom <= 50000000
) ca
OPTION (MAXRECURSION 0);

คุณสามารถดูส่วนที่เกิดซ้ำตามปกติพร้อมกับดัชนีที่ค้นหาค่าที่แตกต่างกันทั้งหมด:

แผนแบบสอบถาม 1

DiffOfColumnsข้อบกพร่องด้วยวิธีนี้ก็คือว่ามันเริ่มที่จะได้รับช้าเมื่อมีค่าแตกต่างกันมากเกินไปสำหรับ ลองทำแบบทดสอบกันดู แต่ใช้CRYPT_GEN_RANDOM(2)CRYPT_GEN_RANDOM(1)แทน

DROP TABLE IF EXISTS dbo.MyTableBigDiff;

CREATE TABLE dbo.MyTableBigDiff
(
Id        INT IDENTITY PRIMARY KEY,
RangeFrom INT NOT NULL,
RangeTo   INT NOT NULL,
CHECK (RangeTo > RangeFrom)
);

WITH RandomNumbers
     AS (SELECT TOP 10000000 ABS(CRYPT_GEN_RANDOM(4)%100000000) AS Num
         FROM   sys.all_objects o1,
                sys.all_objects o2,
                sys.all_objects o3,
                sys.all_objects o4)
INSERT INTO dbo.MyTableBigDiff
            (RangeFrom,
             RangeTo)
SELECT Num,
       Num + 1 + CRYPT_GEN_RANDOM(2) -- note the 2
FROM   RandomNumbers;


ALTER TABLE dbo.MyTableBigDiff ADD DiffOfColumns AS RangeTo-RangeFrom;

CREATE INDEX IXDIFF ON dbo.MyTableBigDiff (DiffOfColumns,RangeFrom) INCLUDE (RangeTo);

แบบสอบถามเดียวกันนี้พบแถว 65536 จากส่วนที่เรียกซ้ำและใช้ CPU 823 ms ในเครื่องของฉัน มี PAGELATCH_SH รออยู่และสิ่งเลวร้ายอื่น ๆ เกิดขึ้น ฉันสามารถปรับปรุงประสิทธิภาพการทำงานโดย bucketing ค่าต่างเพื่อให้จำนวนของค่าที่ไม่ซ้ำกันภายใต้การควบคุมและปรับ bucketing CROSS APPLYในที่ สำหรับชุดข้อมูลนี้ฉันจะลอง 256 buckets:

ALTER TABLE dbo.MyTableBigDiff ADD DiffOfColumns_bucket256 AS CAST(CEILING((RangeTo-RangeFrom) / 256.) AS INT);

CREATE INDEX [IXDIFF😎] ON dbo.MyTableBigDiff (DiffOfColumns_bucket256, RangeFrom) INCLUDE (RangeTo);

วิธีหนึ่งในการหลีกเลี่ยงการเพิ่มแถว (ตอนนี้ฉันเปรียบเทียบกับค่าที่ปัดเศษแทนค่าจริง) คือการกรองRangeTo:

CROSS APPLY (
    SELECT mt.Id, mt.RangeFrom, mt.RangeTo
    FROM dbo.MyTableBigDiff mt
    WHERE mt.DiffOfColumns_bucket256 = rcte.DiffOfColumns_bucket256
    AND mt.RangeFrom >= 50000000 - (256 * rcte.DiffOfColumns_bucket256)
    AND mt.RangeFrom <= 50000000
    AND mt.RangeTo >= 50000000
) ca

แบบสอบถามแบบเต็มตอนนี้ใช้เวลา 6 ms บนเครื่องของฉัน


8

อีกวิธีหนึ่งในการแสดงช่วงจะเป็นจุดบนบรรทัด

ด้านล่างจะย้ายข้อมูลทั้งหมดไปยังตารางใหม่โดยมีช่วงที่แสดงเป็นgeometryประเภทข้อมูล

CREATE TABLE MyTable2
(
Id INT IDENTITY PRIMARY KEY,
Range GEOMETRY NOT NULL,
RangeFrom AS Range.STPointN(1).STX,
RangeTo   AS Range.STPointN(2).STX,
CHECK (Range.STNumPoints() = 2 AND Range.STPointN(1).STY = 0 AND Range.STPointN(2).STY = 0)
);

SET IDENTITY_INSERT MyTable2 ON

INSERT INTO MyTable2
            (Id,
             Range)
SELECT ID,
       geometry::STLineFromText(CONCAT('LINESTRING(', RangeFrom, ' 0, ', RangeTo, ' 0)'), 0)
FROM   MyTable

SET IDENTITY_INSERT MyTable2 OFF 


CREATE SPATIAL INDEX index_name   
ON MyTable2 ( Range )  
USING GEOMETRY_GRID  
WITH (  
BOUNDING_BOX = ( xmin=0, ymin=0, xmax=110000000, ymax=1 ),  
GRIDS = (HIGH, HIGH, HIGH, HIGH),  
CELLS_PER_OBJECT = 16); 

ข้อความค้นหาที่เทียบเท่าเพื่อค้นหาช่วงที่มีค่า50,000,000อยู่ด้านล่าง

SELECT Id,
       RangeFrom,
       RangeTo
FROM   MyTable2
WHERE  Range.STContains(geometry::STPointFromText ('POINT (50000000 0)', 0)) = 1 

การอ่านสำหรับการนี้จะแสดงการปรับปรุงใน10,951จากแบบสอบถามเดิม

Table 'MyTable2'. Scan count 0, logical reads 505, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'extended_index_1797581442_384000'. Scan count 4, logical reads 17, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

อย่างไรก็ตามไม่มีการปรับปรุงอย่างมีนัยสำคัญมากกว่าต้นฉบับในแง่ของเวลาที่ผ่านไปไม่มีการปรับปรุงที่สำคัญมากกว่าเดิมในแง่ของเวลาที่ผ่านไปผลการดำเนินการทั่วไปคือ 250 ms เทียบกับ 252 ms

แผนการดำเนินการมีความซับซ้อนมากขึ้นดังต่อไปนี้

ป้อนคำอธิบายรูปภาพที่นี่

กรณีเดียวที่การเขียนซ้ำมีประสิทธิภาพดีกว่าสำหรับฉันคือการใช้แคชเย็น

น่าผิดหวังในกรณีนี้และยากที่จะแนะนำการเขียนซ้ำนี้ แต่การตีพิมพ์ผลลัพธ์เชิงลบก็มีประโยชน์เช่นกัน


5

เพื่อเป็นการแสดงความเคารพต่อหุ่นยนต์ตัวใหม่ของเราฉันตัดสินใจที่จะดูว่าฟังก์ชั่นใหม่ ish R และ Python สามารถช่วยเราได้ที่นี่หรือไม่ คำตอบคือไม่อย่างน้อยสำหรับสคริปต์ที่ฉันสามารถทำงานได้และส่งคืนผลลัพธ์ที่ถูกต้อง หากใครที่มีความรู้ดีกว่ามาด้วยละก็อย่าลังเลที่จะตบฉัน ราคาของฉันนั้นสมเหตุสมผล

ในการทำเช่นนี้ฉันตั้งค่า VM ด้วย 4 คอร์และ RAM 16 GB โดยคิดว่านี่จะเพียงพอที่จะจัดการกับชุดข้อมูล ~ 200MB

เริ่มจากภาษาที่ไม่มีใน Boston กันเถอะ!

R

EXEC sp_execute_external_script 
@language = N'R', 
@script = N'
tweener = 50000000
MO = data.frame(MartinIn)
MartinOut <- subset(MO, RangeFrom <= tweener & RangeTo >= tweener, select = c("Id","RangeFrom","RangeTo"))
', 
@input_data_1_name = N'MartinIn',
@input_data_1 = N'SELECT Id, RangeFrom, RangeTo FROM dbo.MyTable',
@output_data_1_name = N'MartinOut',
@parallel = 1
WITH RESULT SETS ((ID INT, RangeFrom INT, RangeTo INT));

นี่เป็นช่วงเวลาที่เลวร้าย

Table 'MyTable'. Scan count 1, logical reads 22400

 SQL Server Execution Times:
   CPU time = 3219 ms,  elapsed time = 5349 ms.

แผนปฏิบัติการสวยจืด แต่ผมไม่ทราบว่าทำไมผู้ประกอบการกลางมีการโทรหาเราชื่อ

ถั่ว

ขึ้นไปข้างบนเขียนรหัสด้วยดินสอสี!

หลาม

EXEC sp_execute_external_script 
@language = N'Python', 
@script = N'
import pandas as pd
MO = pd.DataFrame(MartinIn)
tweener = 50000000
MartinOut = MO[(MO.RangeFrom <= tweener) & (MO.RangeTo >= tweener)]
', 
@input_data_1_name = N'MartinIn',
@input_data_1 = N'SELECT Id, RangeFrom, RangeTo FROM dbo.MyTable',
@output_data_1_name = N'MartinOut',
@parallel = 1
WITH RESULT SETS ((ID INT, RangeFrom INT, RangeTo INT));

เมื่อคุณคิดว่ามันจะไม่เลวร้ายไปกว่า R:

Table 'MyTable'. Scan count 1, logical reads 22400

 SQL Server Execution Times:
   CPU time = 3797 ms,  elapsed time = 10146 ms.

อีกปากร้ายแผนปฏิบัติการ :

ถั่ว

อืมและ Hmmer

จนถึงตอนนี้ฉันไม่ประทับใจ ฉันรอไม่ไหวที่จะลบ VM นี้


1
คุณสามารถส่งผ่านพารามิเตอร์ได้เช่นกันDECLARE @input INT = 50000001; EXEC dbo.sp_execute_external_script @language = N'R', @script = N'OutputDataSet <- InputDataSet[which(x >= InputDataSet$RangeFrom & x <= InputDataSet$RangeTo) , ]', @parallel = 1, @input_data_1 = N'SELECT Id, RangeFrom, RangeTo FROM dbo.MyTable;', @params = N'@x INT', @x = 50000001 WITH RESULT SETS ( ( Id INT NOT NULL, RangeFrom INT NOT NULL, RangeTo INT NOT NULL ));แต่ประสิทธิภาพการทำงานไม่ดี ฉันใช้ R สำหรับสิ่งที่คุณไม่สามารถทำได้ใน SQL พูดว่าถ้าคุณต้องการทำนายบางสิ่ง
wBob

4

ฉันพบวิธีแก้ปัญหาที่ดีงามโดยใช้คอลัมน์ที่คำนวณแล้ว แต่มันก็ดีสำหรับค่าเดียว ถ้าคุณมีค่าเวทย์มนตร์บางทีก็เพียงพอแล้ว

เริ่มต้นด้วยตัวอย่างที่คุณได้รับจากนั้นปรับเปลี่ยนตาราง:

ALTER TABLE dbo.MyTable
    ADD curtis_jackson 
        AS CONVERT(BIT, CASE 
                            WHEN RangeTo >= 50000000
                            AND RangeFrom < 50000000
                            THEN 1 
                            ELSE 0 
                        END);

CREATE INDEX IX1_redo 
    ON dbo.MyTable (curtis_jackson) 
        INCLUDE (RangeFrom, RangeTo);

แบบสอบถามจะกลายเป็น:

SELECT *
FROM MyTable
WHERE curtis_jackson = 1;

ซึ่งส่งกลับผลลัพธ์เช่นเดียวกับแบบสอบถามเริ่มต้นของคุณ เมื่อปิดแผนการดำเนินการนี่คือสถานะ (ตัดให้สั้นลง):

Table 'MyTable'. Scan count 1, logical reads 3...

SQL Server Execution Times:
  CPU time = 0 ms,  elapsed time = 0 ms.

และนี่คือแผนแบบสอบถาม :

ถั่ว


คุณไม่สามารถเอาชนะการเลียนแบบดัชนีคอลัมน์ / ดัชนีที่กรองแล้วด้วยดัชนีบนได้WHERE (50000000 BETWEEN RangeFrom AND RangeTo) INCLUDE (..)หรือไม่?
ypercubeᵀᴹ

3
@ yper-crazyhat-cubeᵀᴹ - ใช่ CREATE INDEX IX1_redo ON dbo.MyTable (curtis_jackson) INCLUDE (RangeFrom, RangeTo) WHERE RangeTo >= 50000000 AND RangeFrom <= 50000000จะทำงาน. และแบบสอบถามSELECT * FROM MyTable WHERE RangeTo >= 50000000 AND RangeFrom <= 50000000;ใช้มัน - ดังนั้นจึงไม่จำเป็นมากสำหรับเคอร์ติสที่น่าสงสาร
มาร์ตินสมิ ธ

3

ทางออกของฉันอยู่บนพื้นฐานของการสังเกตว่าช่วงเวลาที่มีความกว้างสูงสุดที่รู้จักกันW สำหรับข้อมูลตัวอย่างนี่คือหนึ่งไบต์หรือ 256 จำนวนเต็ม ดังนั้นสำหรับการค้นหาที่กำหนดค่าพารามิเตอร์Pเรารู้เฮ้าส์ขนาดเล็กที่สุดที่สามารถอยู่ในชุดผลที่ได้คือP - W การเพิ่มสิ่งนี้ลงในเพรดิเคตให้

declare @P int = 50000000;
declare @W int = 256;

select
    *
from MyTable
where @P between RangeFrom and RangeTo
and RangeFrom >= (@P - @W);

เมื่อพิจารณาถึงการตั้งค่าดั้งเดิมและค้นหาเครื่องของฉัน (64 บิต Windows 10, ไฮเปอร์เธรดแบบเธรด 4-Core i7, 2.8GHz, 16GB RAM) จะแสดงผล 13 แถว แบบสอบถามนั้นใช้การค้นหาดัชนีแบบขนานของดัชนี (RangeFrom, RangeTo) แบบสอบถามที่แก้ไขแล้วยังดำเนินการค้นหาดัชนีแบบขนานบนดัชนีเดียวกัน

การวัดสำหรับข้อความค้นหาต้นฉบับและข้อความที่แก้ไขคือ

                          Original  Revised
                          --------  -------
Stats IO Scan count              9        6
Stats IO logical reads       11547        6

Estimated number of rows   1643170  1216080
Number of rows read        5109666       29
QueryTimeStats CPU             344        2
QueryTimeStats Elapsed          53        0

สำหรับแบบสอบถามต้นฉบับจำนวนแถวที่อ่านจะเท่ากับจำนวนแถวที่น้อยกว่าหรือเท่ากับ @P เคียวรีเครื่องมือเพิ่มประสิทธิภาพ (QO) ไม่มีทางเลือกอื่น แต่อ่านได้ทั้งหมดเนื่องจากไม่สามารถกำหนดล่วงหน้าได้ว่าจะให้แถวเหล่านี้ตอบสนองได้อย่างไร ดัชนีหลายคอลัมน์บน (RangeFrom, RangeTo) ไม่มีประโยชน์ในการกำจัดแถวที่ไม่ตรงกับ RangeTo เนื่องจากไม่มีความสัมพันธ์กันระหว่างคีย์ดัชนีแรกและวินาทีที่สามารถใช้ได้ ตัวอย่างเช่นแถวแรกอาจมีช่วงเวลาขนาดเล็กและถูกกำจัดในขณะที่แถวที่สองมีช่วงเวลาที่มีขนาดใหญ่และถูกส่งคืนหรือกลับกัน

ในความพยายามล้มเหลวครั้งหนึ่งฉันพยายามให้ความมั่นใจผ่านข้อ จำกัด การตรวจสอบ:

alter table MyTable with check
add constraint CK_MyTable_Interval
check
(
    RangeTo <= RangeFrom + 256
);

มันไม่ต่างอะไรเลย

ด้วยการรวมความรู้ภายนอกของฉันเกี่ยวกับการกระจายข้อมูลเข้าสู่ภาคแสดงฉันสามารถทำให้ QO ข้ามแถว RangeFrom ที่มีมูลค่าต่ำซึ่งไม่สามารถเป็นส่วนหนึ่งของชุดผลลัพธ์ได้และสำรวจคอลัมน์นำของดัชนีไปยังแถวที่ยอมรับได้ สิ่งนี้แสดงให้เห็นในภาคการค้นหาที่แตกต่างกันสำหรับแต่ละแบบสอบถาม

ในการโต้แย้งกระจกขีด จำกัด บนของ RangeTo เป็นP + WW อย่างไรก็ตามสิ่งนี้ไม่มีประโยชน์เนื่องจากไม่มีความสัมพันธ์ระหว่าง RangeFrom และ RangeTo ที่จะอนุญาตให้คอลัมน์ต่อท้ายของดัชนีหลายคอลัมน์เพื่อกำจัดแถว ดังนั้นจึงไม่มีประโยชน์จากการเพิ่มข้อนี้ลงในแบบสอบถาม

วิธีนี้จะได้ประโยชน์มากที่สุดจากช่วงเวลาที่มีขนาดเล็ก เมื่อขนาดช่วงเวลาที่เป็นไปได้เพิ่มขึ้นจำนวนแถวที่มีค่าต่ำจะลดลงแม้ว่าบางคนจะยังคงถูกข้ามไป ในกรณีที่ จำกัด ด้วยช่วงเวลาที่มีขนาดใหญ่เท่ากับช่วงข้อมูลวิธีการนี้จะไม่เลวร้ายยิ่งกว่าแบบสอบถามแบบดั้งเดิม

ฉันขออภัยในความผิดพลาดใด ๆ ก็ตามที่อาจเกิดขึ้นในคำตอบนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.