Tại sao LEN () hoạt động kém đánh giá thấp cardinality trong SQL Server 2014?


26

Tôi có một bảng với một cột chuỗi và một vị từ kiểm tra các hàng có độ dài nhất định. Trong SQL Server 2014, tôi thấy ước tính 1 hàng bất kể độ dài tôi đang kiểm tra. Điều này mang lại các kế hoạch rất kém vì thực sự có hàng ngàn hoặc thậm chí hàng triệu hàng và SQL Server đang chọn đặt bảng này ở phía bên ngoài của một vòng lặp lồng nhau.

Có lời giải thích nào cho ước tính cardinality là 1.0003 cho SQL Server 2014 trong khi SQL Server 2012 ước tính 31.622 hàng không? Có một cách giải quyết tốt?

Đây là một bản sao ngắn của vấn đề:

-- Create a table with 1MM rows of dummy data
CREATE TABLE #customers (cust_nbr VARCHAR(10) NOT NULL)
GO

INSERT INTO #customers WITH (TABLOCK) (cust_nbr)
    SELECT TOP 1000000 
        CONVERT(VARCHAR(10),
        ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) AS cust_nbr
    FROM master..spt_values v1
    CROSS JOIN master..spt_values v2
GO

-- Looking for string of a certain length.
-- While both CEs yield fairly poor estimates, the 2012 CE is much
-- more conservative (higher estimate) and therefore much more likely
-- to yield an okay plan rather than a drastically understimated loop join.
-- 2012: 31,622 rows estimated, 900K rows actual
-- 2014: 1 row estimated, 900K rows actual
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION (QUERYTRACEON 9481) -- Optionally, use 2012 CE
GO

Đây là một kịch bản đầy đủ hơn cho thấy các bài kiểm tra bổ sung

Tôi cũng đã đọc whitepaper trên Công cụ ước tính Cardinality của SQL Server 2014 , nhưng không tìm thấy bất cứ điều gì làm rõ tình hình.

Câu trả lời:


20

Đối với CE kế thừa, tôi thấy ước tính dành cho 3,16228% số hàng - và đó là một "số ma thuật" heuristic được sử dụng cho cột = vị ngữ nghĩa đen (có các phương pháp phỏng đoán khác dựa trên cấu trúc vị ngữ - nhưng LENđược bọc quanh cột cho kết quả CE kế thừa phù hợp với khung đoán này). Bạn có thể xem các ví dụ về điều này trên một bài đăng trên Đoán chọn lọc mà không có Thống kê của Joe Sack và Ước tính so sánh không đổi liên tục của Ian Jose.

-- Legacy CE: 31622.8 rows
SELECT  COUNT(*)
FROM    #customers
WHERE   LEN(cust_nbr) = 6
OPTION  ( QUERYTRACEON 9481); -- Legacy CE
GO

Bây giờ đối với hành vi CE mới, có vẻ như điều này hiện được hiển thị cho trình tối ưu hóa (có nghĩa là chúng ta có thể sử dụng số liệu thống kê). Tôi đã trải qua bài tập nhìn vào đầu ra của máy tính bên dưới và bạn có thể xem việc tạo các số liệu thống kê tự động liên quan như một con trỏ:

-- New CE: 1.00007 rows
SELECT  COUNT(*)
FROM    #customers
WHERE   LEN(cust_nbr) = 6
OPTION  ( QUERYTRACEON 2312 ); -- New CE
GO

-- View New CE behavior with 2363 (for supported option use XEvents)
SELECT  COUNT(*)
FROM    #customers
WHERE   LEN(cust_nbr) = 6
OPTION  (QUERYTRACEON 2312, QUERYTRACEON 2363, QUERYTRACEON 3604, RECOMPILE); -- New CE
GO

/*
Loaded histogram for column QCOL:
[tempdb].[dbo].[#customers].cust_nbr from stats with id 2
Using ambient cardinality 1e+006 to combine distinct counts:
  999927

Combined distinct count: 999927
Selectivity: 1.00007e-006
Stats collection generated:
  CStCollFilter(ID=2, CARD=1.00007)
      CStCollBaseTable(ID=1, CARD=1e+006 TBL: #customers)

End selectivity computation
*/

EXEC tempdb..sp_helpstats '#customers';


--Check out AVG_RANGE_ROWS values (for example - plenty of ~ 1)
DBCC SHOW_STATISTICS('tempdb..#customers', '_WA_Sys_00000001_B0368087');
--That's my Stats name yours is subject to change

Thật không may, logic dựa trên ước tính số lượng giá trị riêng biệt, không được điều chỉnh cho hiệu ứng của LENhàm.

Cách giải quyết có thể

Bạn có thể lấy ước tính dựa trên bộ ba theo cả hai mô hình CE bằng cách viết lại LENdưới dạng LIKE:

SELECT COUNT_BIG(*)
FROM #customers AS C
WHERE C.cust_nbr LIKE REPLICATE('_', 6);

Kế hoạch THÍCH


Thông tin về Trace Flag được sử dụng:

  • 2363: hiển thị rất nhiều thông tin, bao gồm cả số liệu thống kê đang được tải.
  • 3604: in đầu ra của các lệnh DBCC sang tab thông báo.

13

Có lời giải thích nào cho ước tính cardinality là 1.0003 cho SQL 2014 trong khi SQL 2012 ước tính 31.622 hàng không?

Tôi nghĩ rằng câu trả lời của @ Zane bao gồm phần này khá tốt.

Có một cách giải quyết tốt?

Bạn có thể thử tạo Cột được tính không liên tục cho LEN(cust_nbr)và (tùy chọn) tạo Chỉ mục không được nhóm trên Cột được tính đó. Điều đó sẽ giúp bạn có được số liệu thống kê chính xác.

Tôi đã làm một số thử nghiệm và đây là những gì tôi tìm thấy:

  • Số liệu thống kê được tạo tự động trên Cột không tính toán không tồn tại, khi không có chỉ mục nào được xác định trên đó.
  • Việc thêm Chỉ mục không được nhóm vào Cột được tính toán không chỉ không giúp ích mà còn thực sự làm giảm hiệu suất một chút. CPU cao hơn một chút và thời gian trôi qua. Chi phí ước tính cao hơn một chút (bất cứ giá trị nào).
  • Tạo Cột được tính là PERSISTED(không có Chỉ mục) tốt hơn hai biến thể còn lại. Hàng ước tính là chính xác hơn. CPU và thời gian trôi qua tốt hơn (như mong đợi vì nó không phải tính toán bất cứ thứ gì trên mỗi hàng).
  • Tôi không thể tạo Chỉ mục được lọc hoặc Thống kê được lọc trên Cột được tính (do nó được tính toán), ngay cả khi đó là PERSISTED:-(

1
Cảm ơn sự so sánh kỹ lưỡng giữa kiên trì và không. Thật tốt khi biết rằng ngay cả khi cột được tính toán bền bỉ có lợi thế của nó, thì việc không kiên trì có thể là một chiến thắng rất nhanh với rất ít chi phí trong một số trường hợp khi thống kê về biểu thức có lợi.
Geoff Patterson
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.