Đừng hiểu sai ý tôi - Tôi thích thuộc tính Actual Rows Read mà chúng tôi đã thấy trong kế hoạch thực thi của SQL Server vào cuối năm 2015. Nhưng trong SQL Server 2016 SP1, chưa đầy hai tháng trước (và coi như chúng ta đã đón Giáng sinh ở giữa, tôi không nghĩ là nhiều thời gian kể từ đó được tính), chúng tôi có một bổ sung thú vị khác - Số lượng hàng ước tính được đọc (ồ, và điều này phần nào nằm ở mục Kết nối mà tôi đã gửi, cả hai đều chứng minh rằng các Mục kết nối đều đáng để gửi và làm cho bài đăng này đủ điều kiện cho Thứ Ba T-SQL của tháng này, được tổ chức bởi Brent Ozar (@brento) về chủ đề Kết nối các mục ).
Hãy tóm tắt lại một chút… khi SQL Engine truy cập dữ liệu trong một bảng, nó sử dụng thao tác Quét hoặc thao tác Tìm kiếm. Và trừ khi Seek đó có một Vị từ tìm kiếm có thể truy cập nhiều nhất một hàng (vì nó đang tìm kiếm một kết quả khớp bình đẳng trên một tập hợp các cột - có thể chỉ là một cột duy nhất - được biết là duy nhất), thì Seek sẽ thực hiện RangeScan và hoạt động giống như Scan, chỉ trên tập hợp con của các hàng được đáp ứng bởi Tìm kiếm vị từ.
Các hàng được đáp ứng bởi Tìm kiếm dự đoán (trong trường hợp RangeScan của hoạt động Tìm kiếm) hoặc tất cả các hàng trong bảng (trong trường hợp hoạt động Quét) về cơ bản được xử lý theo cùng một cách. Cả hai đều có thể bị kết thúc sớm nếu không có thêm hàng nào được yêu cầu từ toán tử bên trái của nó, ví dụ:nếu toán tử Hàng đầu ở đâu đó đã nắm đủ hàng hoặc nếu Toán tử hợp nhất không còn hàng nào để so khớp. Và cả hai có thể được lọc thêm bằng một Vị từ dư (được hiển thị dưới dạng thuộc tính 'Vị từ') trước khi các hàng thậm chí được phân phối bởi toán tử Quét / Tìm kiếm. Thuộc tính "Số lượng hàng" và "Số lượng hàng ước tính" sẽ cho chúng tôi biết số lượng hàng dự kiến được tạo ra bởi toán tử, nhưng chúng tôi không có bất kỳ thông tin nào về cách các hàng có thể được lọc bằng chỉ Tìm kiếm dự đoán. Chúng ta có thể thấy TableCardinality, nhưng điều này chỉ thực sự hữu ích cho các toán tử Quét, nơi có khả năng Quét có thể xem qua toàn bộ bảng để tìm các hàng mà nó cần. Nó hoàn toàn không hữu ích cho Seeks.
Truy vấn mà tôi đang chạy ở đây chống lại cơ sở dữ liệu WideWorldImporters và là:
CHỌN ĐẾM (*) TỪ Sales.OrdersWHERE Sales NHÂNPersonID =7AND NĂM (OrderDate) =2013AND MONTH (OrderDate) =4;
Hơn nữa, tôi có một chỉ số đang chơi:
TẠO CHỈ SỐ KHÔNG ĐƯỢC ĐIỀU CHỈNH rf_Orders_SalesPeople_OrderDate ON Sales.Orders (Người bán hàngPersonID, OrderDate);
Chỉ mục này đang bao trùm - truy vấn không cần bất kỳ cột nào khác để nhận câu trả lời - và đã được thiết kế để có thể sử dụng Dự đoán tìm kiếm trên SaleswomanPersonID, nhanh chóng lọc dữ liệu xuống một phạm vi nhỏ hơn. Các chức năng trên OrderDate có nghĩa là hai vị từ cuối cùng đó không thể được sử dụng trong Tìm kiếm vị từ, vì vậy chúng được chuyển xuống vị trí còn lại để thay thế. Một truy vấn tốt hơn sẽ lọc những ngày đó bằng OrderDate> ='20130401' AND OrderDate <'20130501', nhưng tôi đang tưởng tượng ra một tình huống quá phổ biến ở đây…
Bây giờ, nếu tôi chạy truy vấn, tôi có thể thấy tác động của Dự đoán phần dư. Plan Explorer thậm chí còn đưa ra cảnh báo hữu ích mà tôi đã viết trước đây.
Tôi có thể thấy rất rõ ràng rằng RangeScan là 7.276 hàng và Dự đoán phần dư lọc điều này xuống còn 149. Plan Explorer hiển thị thêm thông tin về điều này trên chú giải công cụ:
Nhưng nếu không chạy truy vấn, tôi không thể thấy thông tin đó. Nó chỉ đơn giản là không có ở đó. Các thuộc tính trong kế hoạch ước tính không có:
Và tôi chắc chắn rằng tôi không cần phải nhắc bạn - thông tin này cũng không có trong bộ nhớ cache của gói. Lấy kế hoạch từ bộ nhớ cache bằng cách sử dụng:
CHỌN p.query_plan, t.textFROM sys.dm_exec_cached_plans cCROSS ÁP DỤNG sys.dm_exec_query_plan (c.plan_handle) pCROSS ÁP DỤNG sys.dm_exec_sql_text (c.plan_handle) tWHERE% YEAR t.text;Tôi đã mở nó ra, và chắc chắn, không có dấu hiệu của giá trị 7.276 đó. Nó trông giống như kế hoạch ước tính mà tôi vừa trình bày.
Lấy các kế hoạch ra khỏi bộ nhớ cache là nơi các giá trị ước tính trở thành giá trị của riêng chúng. Không chỉ là tôi không muốn thực sự chạy các truy vấn có thể tốn kém trên cơ sở dữ liệu khách hàng. Truy vấn bộ nhớ cache của gói là một chuyện, nhưng chạy các truy vấn để nhận dữ liệu thực tế - điều đó khó hơn rất nhiều.
Với SQL 2016 SP1 được cài đặt, nhờ vào mục Kết nối đó, giờ đây tôi có thể thấy thuộc tính Số lượng hàng ước tính sẽ đọc trong các kế hoạch ước tính và trong bộ đệm ẩn của kế hoạch. Chú giải công cụ toán tử hiển thị ở đây được lấy từ bộ nhớ cache và tôi có thể dễ dàng thấy thuộc tính Ước tính hiển thị 7.276, cũng như cảnh báo còn lại:
Đây là điều mà tôi có thể làm trên hộp khách hàng, tìm kiếm trong bộ nhớ cache các tình huống trong các kế hoạch có vấn đề trong đó tỷ lệ giữa Số lượng hàng ước tính sẽ được đọc và Số lượng hàng ước tính không lớn. Về khả năng, ai đó có thể thực hiện một quy trình kiểm tra mọi kế hoạch trong bộ nhớ cache, nhưng đó không phải là điều tôi đã làm.
Đọc tinh tế sẽ nhận thấy rằng các Hàng thực tế xuất phát từ toán tử này là 149, nhỏ hơn nhiều so với ước tính 1382,56. Nhưng khi tôi đang tìm các Dự đoán còn lại đang phải kiểm tra quá nhiều hàng, tỷ lệ 1.382,56:7,276 vẫn rất quan trọng.
Bây giờ chúng tôi nhận thấy rằng truy vấn này không hiệu quả mà thậm chí không cần chạy nó, cách để khắc phục nó là đảm bảo rằng Residual Predicate đủ SARGable. Truy vấn này…
CHỌN ĐẾM (*) TỪ Sales.OrdersWHERE Người bán hàngPersonID =7 VÀ Ngày đặt hàng> ='20130401' VÀ Ngày đặt hàng <'20130501';… Cho kết quả tương tự và không có Vị từ dư. Trong trường hợp này, giá trị Số lượng hàng được ước tính sẽ đọc giống với Số lượng hàng ước tính và tính không hiệu quả sẽ không còn nữa:
Như đã đề cập trước đó, bài đăng này là một phần của Thứ Ba T-SQL của tháng này. Tại sao không đến đó để xem những yêu cầu tính năng nào khác đã được cấp gần đây?