Tôi sẽ giả định rằng bạn đã sai lệch dữ liệu, rằng bạn không muốn sử dụng gợi ý truy vấn để buộc trình tối ưu hóa phải làm gì và bạn cần có hiệu suất tốt cho tất cả các giá trị đầu vào có thể có @Id
. Bạn có thể nhận được một gói truy vấn được đảm bảo chỉ cần một vài lần đọc logic cho bất kỳ giá trị đầu vào nào có thể nếu bạn sẵn sàng tạo cặp chỉ mục sau (hoặc tương đương với chúng):
CREATE INDEX GetMinSomeTimestamp ON dbo.MyTable (Id, SomeTimestamp) WHERE SomeBit = 1;
CREATE INDEX GetMaxSomeInt ON dbo.MyTable (Id, SomeInt) WHERE SomeBit = 1;
Dưới đây là dữ liệu thử nghiệm của tôi. Tôi đặt 13 hàng M vào bảng và làm cho một nửa trong số chúng có giá trị '3A35EA17-CE7E-4637-8319-4C517B6E48CA'
cho Id
cột.
DROP TABLE IF EXISTS dbo.MyTable;
CREATE TABLE dbo.MyTable (
Id uniqueidentifier,
SomeTimestamp DATETIME2,
SomeInt INT,
SomeBit BIT,
FILLER VARCHAR(100)
);
INSERT INTO dbo.MyTable WITH (TABLOCK)
SELECT NEWID(), CURRENT_TIMESTAMP, 0, 1, REPLICATE('Z', 100)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
INSERT INTO dbo.MyTable WITH (TABLOCK)
SELECT '3A35EA17-CE7E-4637-8319-4C517B6E48CA', CURRENT_TIMESTAMP, 0, 1, REPLICATE('Z', 100)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Truy vấn này thoạt nhìn có vẻ hơi lạ:
DECLARE @Id UNIQUEIDENTIFIER = '3A35EA17-CE7E-4637-8319-4C517B6E48CA'
SELECT
@Id,
st.SomeTimestamp,
si.SomeInt
FROM (
SELECT TOP (1) SomeInt, Id
FROM dbo.MyTable
WHERE Id = @Id
AND SomeBit = 1
ORDER BY SomeInt DESC
) si
CROSS JOIN (
SELECT TOP (1) SomeTimestamp, Id
FROM dbo.MyTable
WHERE Id = @Id
AND SomeBit = 1
ORDER BY SomeTimestamp ASC
) st;
Nó được thiết kế để tận dụng thứ tự của các chỉ mục để tìm giá trị tối thiểu hoặc tối đa với một vài lần đọc logic. Có CROSS JOIN
để có kết quả chính xác khi không có bất kỳ hàng phù hợp nào cho @Id
giá trị. Ngay cả khi tôi lọc theo giá trị phổ biến nhất trong bảng (khớp với 6,5 triệu hàng) tôi chỉ nhận được 8 lần đọc logic:
Bảng 'MyTable'. Quét số 2, đọc logic 8
Đây là kế hoạch truy vấn:
Cả hai chỉ mục tìm kiếm 0 hoặc 1 hàng. Nó cực kỳ hiệu quả, nhưng việc tạo hai chỉ mục có thể là quá mức cần thiết cho kịch bản của bạn. Bạn có thể xem xét các chỉ số sau thay thế:
CREATE INDEX CoveringIndex ON dbo.MyTable (Id) INCLUDE (SomeTimestamp, SomeInt) WHERE SomeBit = 1;
Bây giờ kế hoạch truy vấn cho truy vấn ban đầu (với một MAXDOP 1
gợi ý tùy chọn ) trông hơi khác một chút:
Việc tra cứu chính không còn cần thiết nữa. Với đường dẫn truy cập tốt hơn sẽ hoạt động tốt cho tất cả các đầu vào, bạn không cần phải lo lắng về trình tối ưu hóa chọn gói truy vấn sai do vectơ mật độ. Tuy nhiên, truy vấn và chỉ mục này sẽ không hiệu quả như truy vấn khác nếu bạn tìm kiếm trên một phổ biến@Id
giá trị .
Bảng 'MyTable'. Quét số 1, đọc logic 33757