Tối ưu hóa tìm kiếm phạm vi số (khoảng) trong SQL Server


18

Câu hỏi này tương tự như Tối ưu hóa phạm vi tìm kiếm IP? nhưng cái đó bị giới hạn trong SQL Server 2000.

Giả sử tôi có 10 triệu phạm vi được lưu trữ tạm thời trong một bảng có cấu trúc và dân cư như dưới đây.

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 

Tôi cần biết tất cả các phạm vi có chứa giá trị 50,000,000. Tôi thử truy vấn sau

SELECT *
FROM MyTable
WHERE 50000000 BETWEEN RangeFrom AND RangeTo

SQL Server cho thấy đã có 10.951 lượt đọc logic và gần 5 triệu hàng đã được đọc để trả về 12 kết quả khớp.

nhập mô tả hình ảnh ở đây

Tôi có thể cải thiện hiệu suất này không? Bất kỳ sự tái cấu trúc của bảng hoặc các chỉ mục bổ sung là tốt.


Nếu tôi hiểu chính xác việc thiết lập bảng, bạn sẽ chọn các số ngẫu nhiên thống nhất để tạo thành các phạm vi của mình, không có ràng buộc về "kích thước" của từng phạm vi. Và thăm dò của bạn là cho giữa phạm vi tổng thể 1..100M. Trong trường hợp đó - không phân cụm rõ ràng do tính ngẫu nhiên thống nhất - Tôi không biết tại sao một chỉ mục ở giới hạn dưới hoặc giới hạn trên sẽ hữu ích. Bạn có thể giải thích điều đó không?
davidbak

@davidbak các chỉ mục thông thường trên bảng này thực sự không hữu ích trong trường hợp xấu nhất vì nó phải quét một nửa phạm vi do đó yêu cầu cải thiện tiềm năng trên nó. Có một sự cải thiện tốt trong câu hỏi được liên kết cho SQL Server 2000 với việc giới thiệu "hạt" Tôi hy vọng các chỉ mục không gian có thể giúp đỡ ở đây vì chúng hỗ trợ containscác truy vấn và trong khi chúng hoạt động tốt trong việc giảm lượng dữ liệu đọc mà chúng dường như thêm vào trên cao mà chống lại điều này.
Martin Smith

Tôi không có cơ sở để thử nó - nhưng tôi tự hỏi nếu hai chỉ mục - một ở giới hạn dưới, một ở phía trên - và sau đó là một kết nối bên trong - sẽ cho phép trình tối ưu hóa truy vấn hoạt động.
davidbak

Câu trả lời:


11

Cột cửa hàng ở đây rất gan so với chỉ số không bao gồm quét một nửa bảng. Một chỉ mục kho lưu trữ cột không bao gồm cung cấp hầu hết các lợi ích nhưng chèn dữ liệu theo thứ tự vào một chỉ mục kho lưu trữ cột thậm chí còn tốt hơn.

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);

Theo thiết kế, tôi có thể loại bỏ nhóm hàng trên RangeFromcột sẽ loại bỏ một nửa số nhóm hàng của tôi. Nhưng do tính chất của dữ liệu, tôi cũng nhận được loại bỏ nhóm hàng trên RangeTocột:

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

Đối với các bảng lớn hơn có nhiều dữ liệu khác nhau, có nhiều cách khác nhau để tải dữ liệu để đảm bảo loại bỏ nhóm hàng tốt nhất có thể trên cả hai cột. Đối với dữ liệu của bạn nói riêng, truy vấn mất 1 ms.


yep chắc chắn tìm kiếm các phương pháp khác để xem xét mà không hạn chế 2000. Không nghe như thế sẽ bị đánh.
Martin Smith

9

Paul White đã chỉ ra một câu trả lời cho một câu hỏi tương tự có chứa một liên kết đến một bài viết thú vị của Itzik Ben Gan . Điều này mô tả mô hình "Cây khoảng thời gian quan hệ tĩnh" cho phép thực hiện điều này một cách hiệu quả.

Tóm lại, cách tiếp cận này liên quan đến việc lưu trữ giá trị được tính toán ("forknode") dựa trên các giá trị khoảng trong hàng. Khi tìm kiếm các phạm vi giao nhau với phạm vi khác, có thể xác định trước các giá trị forode có thể có mà các hàng khớp phải có và sử dụng kết quả này để tìm kết quả với tối đa 31 thao tác tìm kiếm (bên dưới hỗ trợ các số nguyên trong phạm vi 0 đến tối đa 32 đã ký bit int)

Dựa vào đó tôi tái cấu trúc bảng như dưới đây.

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 

Và sau đó sử dụng truy vấn sau (bài viết đang tìm các khoảng giao nhau, vì vậy việc tìm một khoảng có chứa một điểm là một trường hợp suy biến của điều này)

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;

Điều này thường thực thi 1mstrên máy của tôi khi tất cả các trang trong bộ đệm - với số liệu thống kê 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.

và kế hoạch

nhập mô tả hình ảnh ở đây

Lưu ý: Nguồn sử dụng TVF đa tầng thay vì CTE đệ quy để đưa các nút tham gia nhưng vì lợi ích của việc đưa ra câu trả lời của mình, tôi đã chọn cách sau. Để sử dụng sản xuất, tôi có thể sử dụng TVF.


9

Tôi đã có thể tìm thấy một cách tiếp cận chế độ hàng cạnh tranh với cách tiếp cận N / CCI, nhưng bạn cần biết điều gì đó về dữ liệu của mình. Giả sử rằng bạn có một cột chứa sự khác biệt RangeFromRangeTovà bạn đã lập chỉ mục cho nó cùng với RangeFrom:

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

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

Nếu bạn biết tất cả các giá trị riêng biệt DiffOfColumnsthì bạn có thể thực hiện tìm kiếm mọi giá trị DiffOfColumnsvới bộ lọc phạm vi RangeTođể nhận tất cả dữ liệu liên quan. Ví dụ: nếu chúng ta biết rằng DiffOfColumns= 2 thì các giá trị được phép duy nhất RangeFromlà 49999998, 49999999 và 50000000. Có thể sử dụng đệ quy để lấy tất cả các giá trị riêng biệt DiffOfColumnsvà nó hoạt động tốt cho tập dữ liệu của bạn vì chỉ có 256 giá trị. Truy vấn dưới đây mất khoảng 6 ms trên máy của tôi:

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);

Bạn có thể thấy phần đệ quy thông thường cùng với chỉ mục tìm kiếm cho mọi giá trị riêng biệt:

kế hoạch truy vấn 1

Lỗ hổng với phương pháp này là nó bắt đầu bị chậm khi có quá nhiều giá trị khác biệt DiffOfColumns. Hãy làm bài kiểm tra tương tự, nhưng sử dụng CRYPT_GEN_RANDOM(2)thay vì 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);

Truy vấn tương tự hiện tìm thấy 65536 hàng từ phần đệ quy và mất 823 ms CPU trên máy của tôi. Có PAGELATCH_SH chờ đợi và những điều tồi tệ khác đang diễn ra. Tôi có thể cải thiện hiệu suất bằng cách khóa các giá trị khác nhau để giữ số lượng giá trị duy nhất trong tầm kiểm soát và điều chỉnh cho việc khóa trong CROSS APPLY. Đối với tập dữ liệu này, tôi sẽ thử 256 thùng:

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);

Một cách để tránh nhận thêm hàng (hiện tại tôi đang so sánh với giá trị làm tròn thay vì giá trị thực) bằng cách lọc trên 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

Truy vấn đầy đủ bây giờ mất 6 ms trên máy của tôi.


8

Một cách khác để biểu thị một phạm vi sẽ là các điểm trên một dòng.

Dưới đây di chuyển tất cả dữ liệu vào một bảng mới với phạm vi được biểu thị dưới dạng geometrykiểu dữ liệu.

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); 

Truy vấn tương đương để tìm phạm vi chứa giá trị 50,000,000bên dưới.

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

Việc đọc cho điều này cho thấy một sự cải thiện 10,951từ truy vấn ban đầu.

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.

Tuy nhiên , không có sự cải thiện đáng kể so với bản gốc về thời gian trôi qua . Kết quả thực hiện điển hình là 250 ms so với 252 ms.

Kế hoạch thực hiện phức tạp hơn như dưới đây

nhập mô tả hình ảnh ở đây

Trường hợp duy nhất mà việc viết lại đáng tin cậy thực hiện tốt hơn đối với tôi là với một bộ đệm lạnh.

Vì vậy, đáng thất vọng trong trường hợp này và khó có thể đề nghị viết lại này nhưng công bố kết quả tiêu cực cũng có thể hữu ích.


5

Như một sự tôn kính đối với các lớp phủ robot mới của chúng tôi, tôi quyết định xem liệu bất kỳ chức năng R và Python mới nào có thể giúp chúng tôi ra khỏi đây không. Câu trả lời là không, ít nhất là đối với các tập lệnh mà tôi có thể làm việc và trả về kết quả chính xác. Nếu bất cứ ai có kiến ​​thức tốt hơn xuất hiện, tốt, hãy thoải mái đánh đòn tôi. Giá của tôi là hợp lý.

Để làm điều này, tôi đã thiết lập một máy ảo có 4 lõi và 16 GB RAM, nghĩ rằng điều này sẽ đủ để xử lý một bộ dữ liệu ~ 200 MB.

Hãy bắt đầu với ngôn ngữ không tồn tại ở 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));

Đây là một thời gian tồi tệ.

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

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

Các kế hoạch thực hiện được khá nhàm chán, mặc dù tôi không biết lý do tại sao các nhà điều hành trung có gọi cho chúng tôi tên.

QUẢ HẠCH

Tiếp theo, mã hóa bằng bút chì màu!

Con trăn

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));

Ngay khi bạn nghĩ nó không thể tệ hơn R:

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

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

Một kế hoạch thực hiện hôi miệng khác :

QUẢ HẠCH

Hmm và Hmmer

Cho đến nay, tôi không ấn tượng. Tôi không thể chờ để xóa VM này.


1
Bạn cũng có thể truyền tham số, ví dụ: 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 ));nhưng hiệu suất không tốt. Tôi sử dụng R cho những thứ bạn không thể làm trong SQL, nói nếu bạn muốn dự đoán điều gì đó.
wBob

4

Tôi đã tìm thấy một giải pháp khá tốt bằng cách sử dụng một cột được tính toán, tuy nhiên nó chỉ tốt cho một giá trị duy nhất. Điều đó đang được nói, nếu bạn có một giá trị ma thuật, có lẽ nó là đủ.

Bắt đầu với mẫu đã cho của bạn, sau đó sửa đổi bảng:

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);

Truy vấn đơn giản trở thành:

SELECT *
FROM MyTable
WHERE curtis_jackson = 1;

Trả về kết quả giống như truy vấn bắt đầu của bạn. Với các kế hoạch thực hiện đã bị tắt, đây là các số liệu thống kê (bị cắt ngắn để ngắn gọn):

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

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

Và đây là kế hoạch truy vấn :

QUẢ HẠCH


Bạn có thể không khắc phục việc bắt chước cột / chỉ mục được tính toán với chỉ mục trên WHERE (50000000 BETWEEN RangeFrom AND RangeTo) INCLUDE (..)không?
ypercubeᵀᴹ

3
@ yper-crazyhat-cubeᵀᴹ - vâng. CREATE INDEX IX1_redo ON dbo.MyTable (curtis_jackson) INCLUDE (RangeFrom, RangeTo) WHERE RangeTo >= 50000000 AND RangeFrom <= 50000000sẽ làm việc Và truy vấn SELECT * FROM MyTable WHERE RangeTo >= 50000000 AND RangeFrom <= 50000000;sử dụng nó - vì vậy không cần nhiều cho người nghèo
Martin Smith

3

Giải pháp của tôi dựa trên quan sát rằng khoảng đó có chiều rộng tối đa W đã biết . Đối với dữ liệu mẫu, đây là một byte hoặc 256 số nguyên. Do đó cho một tìm kiếm giá trị tham số cho P chúng ta biết RangeFrom nhỏ nhất có thể được trong tập kết quả là P - W . Thêm phần này vào vị ngữ

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

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

Đưa ra thiết lập ban đầu và truy vấn máy của tôi (Windows 10 64 bit, i7 siêu tốc 4 nhân, tốc độ 2,8 GHz, RAM 16 GB) trả về 13 hàng. Truy vấn đó sử dụng tìm kiếm chỉ mục song song của chỉ mục (RangeFrom, RangeTo). Truy vấn sửa đổi cũng thực hiện tìm kiếm chỉ mục song song trên cùng một chỉ mục.

Các phép đo cho các truy vấn ban đầu và sửa đổi là

                          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

Đối với truy vấn ban đầu, số lượng hàng được đọc bằng số lượng hàng nhỏ hơn hoặc bằng @P. Trình tối ưu hóa truy vấn (QO) không có lựa chọn nào khác ngoài việc đọc tất cả vì nó không thể xác định trước nếu các hàng này sẽ thỏa mãn vị ngữ. Chỉ mục nhiều cột trên (RangeFrom, RangeTo) không hữu ích trong việc loại bỏ các hàng không khớp với RangeTo vì không có mối tương quan giữa khóa chỉ mục đầu tiên và khóa thứ hai có thể được áp dụng. Ví dụ, hàng đầu tiên có thể có một khoảng nhỏ và bị loại bỏ trong khi hàng thứ hai có một khoảng lớn và được trả lại, hoặc ngược lại.

Trong một lần thử thất bại, tôi đã cố gắng cung cấp sự chắc chắn đó thông qua một ràng buộc kiểm tra:

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

Nó không có sự khác biệt.

Bằng cách kết hợp kiến ​​thức bên ngoài của tôi về phân phối dữ liệu vào vị ngữ, tôi có thể khiến QO bỏ qua các hàng RangeFrom có ​​giá trị thấp, không bao giờ có thể là một phần của tập kết quả và đi qua cột hàng đầu của chỉ mục đến các hàng được chấp nhận. Điều này cho thấy trong các vị từ tìm kiếm khác nhau cho mỗi truy vấn.

Trong một cuộc tranh luận gương giới hạn trên của RangeTo là P + W . Tuy nhiên, điều này không hữu ích vì không có mối tương quan giữa RangeFrom và RangeTo sẽ cho phép cột theo dõi của một chỉ mục nhiều cột để loại bỏ các hàng. Do đó không có lợi ích từ việc thêm mệnh đề này vào truy vấn.

Cách tiếp cận này đạt được hầu hết lợi ích của nó từ kích thước khoảng nhỏ. Vì kích thước khoảng có thể làm tăng số lượng hàng có giá trị thấp bị bỏ qua giảm, mặc dù một số hàng vẫn sẽ bị bỏ qua. Trong trường hợp giới hạn, với khoảng cách lớn như phạm vi dữ liệu, cách tiếp cận này không tệ hơn truy vấn ban đầu (đó là sự thoải mái lạnh lùng mà tôi thừa nhận).

Tôi xin lỗi vì bất cứ lỗi nào có thể tồn tại trong câu trả lời này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.