Điều quan trọng cần nhớ là bạn không đảm bảo tính nhất quán khi bạn thay đổi truy vấn hoặc dữ liệu trong các bảng. Trình tối ưu hóa truy vấn có thể chuyển sang sử dụng một phương pháp ước tính cardinality khác nhau (chẳng hạn như sử dụng mật độ trái ngược với biểu đồ) có thể làm cho hai truy vấn dường như không nhất quán với nhau. Như đã nói, có vẻ như trình tối ưu hóa truy vấn đang đưa ra lựa chọn không hợp lý trong trường hợp của bạn, vì vậy hãy tìm hiểu kỹ.
Bản demo của bạn quá phức tạp nên tôi sẽ xử lý một ví dụ đơn giản hơn mà tôi tin là cho thấy hành vi tương tự. Bắt đầu chuẩn bị dữ liệu và định nghĩa bảng:
DROP TABLE dbo.T1 IF EXISTS;
CREATE TABLE dbo.T1 (FromDate DATE, ToDate DATE, SomeId INT);
INSERT INTO dbo.T1 WITH (TABLOCK)
SELECT TOP 1000 NULL, NULL, 1
FROM master..spt_values v1;
DROP TABLE dbo.T2 IF EXISTS;
CREATE TABLE dbo.T2 (SomeDateTime DATETIME, INDEX IX(SomeDateTime));
INSERT INTO dbo.T2 WITH (TABLOCK)
SELECT TOP 2 NULL
FROM master..spt_values v1
CROSS JOIN master..spt_values v2;
Đây là SELECT
truy vấn để điều tra:
SELECT *
FROM T1
INNER JOIN T2 ON t2.SomeDateTime BETWEEN T1.FromDate AND T1.ToDate
WHERE T1.SomeId = 1;
Truy vấn này đủ đơn giản để chúng tôi có thể tìm ra công thức cho ước tính cardinality mà không có bất kỳ cờ theo dõi nào. Tuy nhiên, tôi sẽ cố gắng sử dụng TF 2363 khi tôi minh họa rõ hơn những gì đang diễn ra trong trình tối ưu hóa. Không rõ liệu tôi có thành công không.
Xác định các biến sau:
C1
= số lượng hàng trong bảng T1
C2
= số lượng hàng trong bảng T2
S1
= độ chọn lọc của T1.SomeId
bộ lọc
Yêu cầu của tôi là ước tính cardinality cho truy vấn trên như sau:
- Khi > = * :
C2
S1
C1
C2
*
với giới hạn dưới của *
S1
S1
C1
- Khi < * :
C2
S1
C1
164.317
* *
với giới hạn trên của
*C2
S1
S1
C1
Chúng ta hãy đi qua một số ví dụ, mặc dù tôi sẽ không đi qua từng ví dụ mà tôi đã thử nghiệm. Đối với chuẩn bị dữ liệu ban đầu, chúng tôi có:
C1
= 1000
C2
= 2
S1
= 1,0
Do đó, ước tính cardinality nên:
2 * 164.317 = 328.634
Ảnh chụp màn hình không thể giả mạo dưới đây chứng minh điều này:
Sử dụng cờ theo dõi không có giấy tờ 2363, chúng ta có thể có được một vài manh mối về những gì đang diễn ra:
Plan for computation:
CSelCalcColumnInInterval
Column: QCOL: [SE_DB2].[dbo].[T1].SomeId
Loaded histogram for column QCOL: [SE_DB2].[dbo].[T1].SomeId from stats with id 2
Selectivity: 1
Stats collection generated:
CStCollFilter(ID=3, CARD=1000)
CStCollBaseTable(ID=1, CARD=1000 TBL: T1)
End selectivity computation
Begin selectivity computation
Input tree:
...
Plan for computation:
CSelCalcSimpleJoinWithUpperBound (Using base cardinality)
CSelCalcOneSided (RIGHT)
CSelCalcCombineFilters_ExponentialBackoff (AND)
CSelCalcFixedFilter (0.3)
CSelCalcFixedFilter (0.3)
Selectivity: 0.164317
Stats collection generated:
CStCollJoin(ID=4, CARD=328.634 x_jtInner)
CStCollFilter(ID=3, CARD=1000)
CStCollBaseTable(ID=1, CARD=1000 TBL: T1)
CStCollBaseTable(ID=2, CARD=2 TBL: T2)
End selectivity computation
Với CE mới, chúng tôi có được ước tính 16% thông thường cho a BETWEEN
. Điều này là do sự thụt lùi theo cấp số nhân với CE 2014 mới. Mỗi bất đẳng thức có ước tính cardinality là 0,3 nên BETWEEN
được tính là 0,3 * sqrt (0,3) = 0,164317. Nhân tỷ lệ chọn lọc 16% với số lượng hàng trong T2 và T1 và chúng tôi có được ước tính của chúng tôi. Có vẻ đủ hợp lý. Hãy tăng số lượng hàng T2
lên 7. Bây giờ chúng ta có các mục sau:
C1
= 1000
C2
= 7
S1
= 1,0
Do đó, ước tính cardinality phải là 1000 vì:
7 * 164.317 = 1150> 1000
Kế hoạch truy vấn xác nhận nó:
Chúng ta có thể có một cái nhìn khác với TF 2363 nhưng có vẻ như sự chọn lọc đã được điều chỉnh đằng sau hậu trường để tôn trọng giới hạn trên. Tôi nghi ngờ rằng điều đó CSelCalcSimpleJoinWithUpperBound
ngăn cản ước tính cardinality vượt quá 1000.
Loaded histogram for column QCOL: [SE_DB2].[dbo].[T1].SomeId from stats with id 2
Selectivity: 1
Stats collection generated:
CStCollFilter(ID=3, CARD=1000)
CStCollBaseTable(ID=1, CARD=1000 TBL: T1)
End selectivity computation
Begin selectivity computation
Input tree:
...
Plan for computation:
CSelCalcSimpleJoinWithUpperBound (Using base cardinality)
CSelCalcOneSided (RIGHT)
CSelCalcCombineFilters_ExponentialBackoff (AND)
CSelCalcFixedFilter (0.3)
CSelCalcFixedFilter (0.3)
Selectivity: 0.142857
Stats collection generated:
CStCollJoin(ID=4, CARD=1000 x_jtInner)
CStCollFilter(ID=3, CARD=1000)
CStCollBaseTable(ID=1, CARD=1000 TBL: T1)
CStCollBaseTable(ID=2, CARD=7 TBL: T2)
Hãy gập T2
xuống 50000 hàng. Bây giờ chúng tôi có:
C1
= 1000
C2
= 50000
S1
= 1,0
Do đó, ước tính cardinality nên:
50000 * 1.0 = 50000
Kế hoạch truy vấn một lần nữa xác nhận nó. Việc ước tính sẽ dễ dàng hơn nhiều sau khi bạn đã tìm ra công thức:
Đầu ra TF:
Loaded histogram for column QCOL: [SE_DB2].[dbo].[T1].SomeId from stats with id 2
Selectivity: 1
Stats collection generated:
CStCollFilter(ID=3, CARD=1000)
CStCollBaseTable(ID=1, CARD=1000 TBL: T1)
...
Plan for computation:
CSelCalcSimpleJoinWithUpperBound (Using base cardinality)
CSelCalcOneSided (RIGHT)
CSelCalcCombineFilters_ExponentialBackoff (AND)
CSelCalcFixedFilter (0.3)
CSelCalcFixedFilter (0.3)
Selectivity: 0.001
Stats collection generated:
CStCollJoin(ID=4, CARD=50000 x_jtInner)
CStCollFilter(ID=3, CARD=1000)
CStCollBaseTable(ID=1, CARD=1000 TBL: T1)
CStCollBaseTable(ID=2, CARD=50000 TBL: T2)
Trong ví dụ này, backoff theo cấp số nhân dường như không liên quan:
5000 * 1000 * 0,001 = 50000.
Bây giờ, hãy thêm 3k hàng vào T1 với SomeId
giá trị 0. Mã để làm như vậy:
INSERT INTO T1 WITH (TABLOCK)
SELECT TOP 3000 NULL, NULL, 0
FROM master..spt_values v1,
master..spt_values v2;
UPDATE STATISTICS T1 WITH FULLSCAN;
Bây giờ chúng tôi có:
C1
= 4000
C2
= 50000
S1
= 0,25
Do đó, ước tính cardinality nên:
50000 * 0,25 = 12500
Kế hoạch truy vấn xác nhận nó:
Đây là hành vi tương tự mà bạn gọi ra trong câu hỏi. Tôi đã thêm các hàng không liên quan vào một bảng và ước tính cardinality giảm. Tại sao điều đó xảy ra? Hãy chú ý đến các đường in đậm:
Loaded histogram for column QCOL: [SE_DB2].[dbo].[T1].SomeId from stats with id 2
Độ chọn lọc: 0,25
Stats collection generated:
CStCollFilter(ID=3, CARD=1000)
CStCollBaseTable(ID=1, CARD=4000 TBL: T1)
End selectivity computation
Begin selectivity computation
Input tree:
...
Plan for computation:
CSelCalcSimpleJoinWithUpperBound (Using base cardinality)
CSelCalcOneSided (RIGHT)
CSelCalcCombineFilters_ExponentialBackoff (AND)
CSelCalcFixedFilter (0.3)
CSelCalcFixedFilter (0.3)
Độ chọn lọc: 0,00025
Stats collection generated:
CStCollJoin(ID=4, CARD=12500 x_jtInner)
CStCollFilter(ID=3, CARD=1000)
CStCollBaseTable(ID=1, CARD=4000 TBL: T1)
CStCollBaseTable(ID=2, CARD=50000 TBL: T2)
End selectivity computation
Có vẻ như ước tính cardinality cho trường hợp này được tính như sau:
C1
* * * / ( * )S1
C2
S1
S1
C1
Hoặc cho ví dụ cụ thể này:
4000 * 0,25 * 50000 * 0,25 / (0,25 * 4000) = 12500
Tất nhiên, công thức chung có thể được đơn giản hóa thành:
C2
* S1
Đó là công thức mà tôi tuyên bố ở trên. Có vẻ như có một số hủy bỏ đang diễn ra mà không nên. Tôi hy vọng tổng số hàng T1
có liên quan đến ước tính.
Nếu chúng ta chèn thêm hàng vào, T1
chúng ta có thể thấy giới hạn dưới trong hành động:
INSERT INTO T1 WITH (TABLOCK)
SELECT TOP 997000 NULL, NULL, 0
FROM master..spt_values v1,
master..spt_values v2;
UPDATE STATISTICS T1 WITH FULLSCAN;
Ước tính cardinality trong trường hợp này là 1000 hàng. Tôi sẽ bỏ qua kế hoạch truy vấn và đầu ra TF 2363.
Cuối cùng, hành vi này khá đáng ngờ nhưng tôi không biết đủ để tuyên bố nếu đó là lỗi hay không. Ví dụ của tôi không khớp chính xác với lời trách móc của bạn nhưng tôi tin rằng tôi đã quan sát hành vi chung đó. Ngoài ra tôi sẽ nói rằng bạn có một chút may mắn với cách bạn chọn dữ liệu ban đầu của mình. Dường như có rất nhiều phỏng đoán đang diễn ra bởi trình tối ưu hóa vì vậy tôi sẽ không quá bận tâm về thực tế rằng truy vấn ban đầu đã trả về 1 triệu hàng khớp chính xác với ước tính.