Tại sao SQL Server chạy truy vấn con cho mỗi hàng của bảng mà nó đủ điều kiện?


7

Truy vấn này chạy trong ~ 21 giây ( kế hoạch thực hiện ):

select 
    a.month
    , count(*) 
from SubqueryTest a 
where a.year = (select max(b.year) from SubqueryTest b)
group by a.month

Khi truy vấn con được thay thế bằng một biến, nó sẽ chạy trong <1 giây ( kế hoạch thực hiện ):

declare @year float
select @year = max(b.year) from SubqueryTest b
select 
    month
    , count(*) 
from SubqueryTest where year = @year group by month

Đánh giá từ kế hoạch thực hiện, lựa chọn phụ "select max ..." được chạy cho mỗi hàng triệu hàng trong "SubqueryTest a:, đó là lý do tại sao phải mất nhiều thời gian như vậy.

Câu hỏi của tôi: Vì lựa chọn phụ là vô hướng, xác định và không tương quan, tại sao trình tối ưu hóa truy vấn không làm những gì tôi đã làm trong ví dụ thứ hai của mình và chạy truy vấn con một lần, lưu trữ kết quả, sau đó sử dụng nó cho truy vấn chính? Tôi chắc chắn rằng có một lỗ hổng trong sự hiểu biết của tôi về SQL Server, nhưng tôi thực sự muốn trợ giúp để lấp đầy nó - một vài giờ với google đã không giúp được gì.

Bảng chỉ hơn 1gb với gần 28 triệu bản ghi:

CREATE TABLE SubqueryTest(
  [pk_id] [int] IDENTITY(1,1) NOT NULL
  , [Year] [float] NULL
  , [Month] [float] NULL PRIMARY KEY CLUSTERED ([pk_id] ASC))

CREATE NONCLUSTERED INDEX idxSubqueryTest ON SubqueryTest ([Year] ASC)

6
Tự hỏi của tôi là tại sao bạn có Yearnhư phao. Xin lỗi, không, điều đó có ý nghĩa với Stardates . Nhưng Monthnhư phao? Thực sự đệm tôi.
ypercubeᵀᴹ

5
Bạn có thể cung cấp các kế hoạch thực hiện?
Martin Smith

@ypercube :) Dữ liệu đằng sau này đến từ Access (vẫn là giao diện người dùng). Nó đã được di chuyển từ việc sử dụng trình hướng dẫn di chuyển Access-to-SQL-Server thích float.
CaptainSlock

@MartinSmith Kế hoạch thực hiện được thêm vào.
CaptainSlock

Kết quả của việc di chuyển truy vấn phụ của bạn từ mệnh đề where vào phần thân của truy vấn là một phép nối bên trong là gì? chọn a.month, đếm (*) từ SubQueryTest a tham gia (chọn max (năm) là [năm] từ SubQueryTest b) là b trên a.year = b.year nhóm của a.month
World Wide DBA

Câu trả lời:


6

Kế hoạch chậm không tính toán MAXcho mỗi hàng trong truy vấn bên ngoài.

Trong thực tế, nó không bao giờ tính toán rõ ràng cả.

Nó đưa ra một kế hoạch tương tự như

WITH CTE
     AS (SELECT TOP(1) WITH TIES *
         FROM   SubqueryTest
         WHERE year IS NOT NULL
         ORDER  BY year desc)
SELECT month,
       count(*)
FROM   CTE
GROUP  BY month 

Gói chậm (Số lượng hàng ước tính)

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

Bạn có một chỉ số không bao phủ trên year ascđể nó quét ngược lại để lấy các hàng trong năm đầu tiên (hiển thị dưới dạng tìm kiếm vì IS NOT NULLvị từ ẩn ).

Thật không may, nó dường như không phân biệt giữa TOP 1TOP 1 WITH TIESkhi ước tính số lượng hàng.

Trong trường hợp này, nó làm cho một sự khác biệt rất lớn. (ước tính 2 lần tra cứu chính so với 4,424.803 thực tế) để bạn có được một kế hoạch không phù hợp.

Gói chậm (Đếm hàng thực tế)

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

Bạn có thể xem xét thêm month vào chỉ mục yeardưới dạng cột chính hoặc cột được bao gồm để làm cho chỉ mục bao trùm. Lợi ích của việc thêm nó dưới dạng cột khóa phụ sẽ là sau đó nó có thể đưa vào tổng hợp luồng mà không cần sắp xếp bổ sung (mặc dù chỉ với 12 giá trị riêng biệt, tổng gộp băm sẽ vẫn ổn).

Một chỉ mục không bao gồm trên một cột không chọn lọc như vậy thực sự khá vô dụng đối với phần lớn các truy vấn. Chỉ mục hoàn toàn bị bỏ qua bởi kế hoạch "nhanh", kết thúc việc quét song song trên toàn bộ bảng và đánh giá vị từ trên tất cả 27.445.400 hàng (ưu tiên thực hiện số lượng tra cứu khổng lồ).

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


Thành thật mà nói, đây có vẻ như là một lỗi hiệu suất / tối ưu hóa đối với tôi. Nó được phép giả định một kết quả ổn định, tĩnh từ truy vấn con và lưu vào bộ đệm, vậy tại sao nó không luôn luôn làm điều đó, bất kể số lượng hàng ước tính là bao nhiêu? Khi nào thì kế hoạch được chọn sẽ tốt hơn bao giờ hết ?
RBarryYoung

@RBarryYoung - Vâng, kế hoạch với biến cũng không tuyệt vời! Nếu chỉ có một số ít các bản sao cho thì TOP 1đây sẽ là kế hoạch tốt nhất. Lỗi với tôi là nó không nhìn vào độ chọn lọc trung bình cho cột đó khi ước tính các hàng choTOP 1 WITH TIES
Martin Smith

Hmmm, điều này vẫn còn khó hiểu / kỳ lạ. Tại sao tôi đang xem xét kế hoạch chậm và một số "Chi phí phụ ước tính" thấp hơn rất nhiều so với chi phí IO và CPU riêng lẻ. Có lẽ bộ não của tôi không hoạt động ngày hôm nay, nhưng điều đó dường như là không thể đối với tôi ...?
RBarryYoung

@RBarryYoung - Bởi vì nó nằm dưới một TOP 1vì vậy họ được thu nhỏ lại cho một mục tiêu hàng. SQL Server ước tính rằng TOPsẽ dừng yêu cầu các hàng sau khi nhận được hàng đầu tiên. Trong thực tế, khi có 4,424.803 hàng đầu tiên trong quá trình quét chỉ mục có cùng năm, phải mất nhiều hơn thế.
Martin Smith

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.