Kế hoạch xấu có một cảnh báo trên bảng tạm thời rằng cột tham gia không có số liệu thống kê. Đưa cái gì?
Có thể có một lý do bí truyền hơn cho việc này, nhưng nhiều khả năng là một thất bại trong việc tạo số liệu thống kê đơn giản. Ví dụ, điều này có thể xảy ra khi tác vụ không nhận được tài nguyên bộ nhớ mà nó cần hoặc khi việc tạo số liệu thống kê đang được điều chỉnh (quá nhiều phần tổng hợp đồng thời). Xem Thống kê giấy trắng của Microsoft được sử dụng bởi Trình tối ưu hóa truy vấn trong Microsoft SQL Server 2008 . Bạn có thể gỡ lỗi này thêm bằng cách xem thông số tự động Profiler hoặc Sự kiện mở rộng và các sự kiện khác cùng một lúc.
Điều đó nói rằng, sẽ cần rất nhiều thông tin và điều tra để đổ lỗi cho việc lựa chọn kế hoạch trước cửa thống kê bảng tạm thời bị mất tích. Ngay cả khi không có số liệu thống kê chi tiết, trình tối ưu hóa vẫn có thể thấy tổng số thẻ của bảng tạm thời và điều này dường như là một yếu tố quan trọng ở đây.
... nhưng có thể được biên dịch lại cho kế hoạch tốt ngay sau đó. Cùng tham số biên dịch.
Các @Months
tham số có thể là giống nhau, nhưng số lượng hàng trong bảng tạm thời (từ quan điểm không rõ View_Feeder
) là khác nhau, và các kế hoạch cung cấp không hiển thị giá trị của @MyId
.
Đi từ thông tin có sẵn: Gói 'tốt' (chỉ ước tính, không có dữ liệu hiệu suất được trình bày) dựa trên bảng tạm thời chứa 4 hàng . "Kế hoạch xấu" dựa trên một bảng tạm thời với 114 hàng . Chắc chắn việc thiếu mật độ và thông tin biểu đồ có thể không hữu ích, nhưng thật dễ dàng để thấy cách trình tối ưu hóa có thể chọn một kế hoạch khác cho 4 so với 114 hàng, mặc dù các mật độ và phân phối không xác định.
Nếu các ước tính về các toán tử kế hoạch không phụ thuộc vào bảng tạm thời bị tắt, thì đây là một tín hiệu mạnh mẽ cho thấy các thống kê bảng chính hiện tại không đại diện cho dữ liệu cơ bản. Việc thiếu thông tin trong câu hỏi làm cho điều này không thể đánh giá.
Tuy nhiên, có thể thấy rằng trình tối ưu hóa đang được yêu cầu lựa chọn giữa các lựa chọn thay thế tối ưu phụ ở đây. Cả hai kế hoạch được trình bày đều thể hiện sự lựa chọn 'rõ ràng là tốt', vì cả hai đều liên quan đến việc tra cứu (thiếu chỉ số 'bao phủ') và lọc muộn (xem tiếp theo). Tra cứu đặc biệt có chi phí cao liên quan đến chúng, điều này phụ thuộc một cách hợp lý vào các ước tính về tim mạch.
Sử dụng chế độ xem hạn chế các lựa chọn tối ưu hóa và gợi ý:
- Khung nhìn chứa một vị trí
GROUP BY
ngăn không cho vị vp.Col3 > DATEADD(MONTH, @Months * -1, GETDATE())
từ bị đẩy xuống, mặc dù phép biến đổi sẽ có hiệu lực trong trường hợp rất cụ thể này.
- Xếp hàng chế độ xem cho truy vấn sẽ cung cấp một cách tự nhiên để lọc cột ngày / giờ trước đó (mặc dù câu hỏi không nêu rõ liệu tái cấu trúc truy vấn có phải là một tùy chọn không).
- Không thể gợi ý một chỉ mục trên một khung nhìn và
FORCESEEK
chỉ cần yêu cầu trình tối ưu hóa tìm bất kỳ gói tìm kiếm chỉ mục nào (không nhất thiết phải sử dụng chỉ mục bạn muốn). Xóa chế độ xem cũng sẽ loại bỏ hạn chế này.
Cho phép vị ngữ đẩy xuống sẽ mở ra các cơ hội lập chỉ mục trên bàn lớn. Ví dụ:
CREATE INDEX give_me_a_good_name
ON dbo.LargeTable (Col1, [Status], Col4)
INCLUDE (P_Id);
... cung cấp đường dẫn truy cập tốt cho truy vấn viết lại:
DECLARE @Date datetime = DATEADD(MONTH, @Months * -1, GETDATE());
SELECT
MT.Col1,
ST.Col2,
MAX(LT.Col4)
FROM #MyTemp AS MT
JOIN dbo.LargeTable AS LT
ON LT.Col1 = MT.Col1
JOIN dbo.SmallTable AS ST
ON ST.P_id = LT.P_Id
WHERE
LT.[Status] IN (3, 4)
AND LT.Col4 > @Date
GROUP BY
MT.Col1,
ST.Col2
OPTION (RECOMPILE);
Một xem xét khác là ảnh hưởng của bộ đệm tạm thời và thống kê bộ đệm như được mô tả trong các bài viết của tôi Các bảng tạm thời trong thủ tục lưu trữ và giải thích bộ đệm tạm thời của bảng tạm thời . Nếu một kế hoạch tốt phụ thuộc vào nội dung hiện tại của đối tượng tạm thời, thì rõ ràng UPDATE STATISTICS #MyTemp;
trước truy vấn chính và thêm OPTION (RECOMPILE)
vào truy vấn chính có thể là một giải pháp tốt.
Ngoài ra, nếu một hình dạng kế hoạch cụ thể luôn tối ưu cho truy vấn này, bạn có sẵn nhiều tùy chọn, bao gồm nhiều gợi ý, hướng dẫn kế hoạch và buộc kế hoạch lưu trữ truy vấn. Bạn có thể thấy rằng sử dụng biến bảng thay vì bảng tạm thời là lựa chọn tốt hơn, vì nó ưu tiên trường hợp cardinality thấp và không cung cấp (hoặc dựa vào) số liệu thống kê.
Tóm lại, có một số cải tiến chung nên được thực hiện trước khi lo lắng về lý do (ảnh hưởng của) số liệu thống kê bị thiếu thường xuyên trên bảng tạm thời:
- Đảm bảo số liệu thống kê là đại diện và hữu ích cho trình tối ưu hóa
- Kiểm tra thực tế so với ước tính cho một loạt các giá trị tham số
- Cung cấp (các) đường dẫn truy cập dữ liệu tốt cho truy vấn bằng cách cải thiện các chỉ mục hiện có
- Xóa chế độ xem nếu có thể; hoặc xem xét một view chế độ xem được tham số hóa '(hàm có giá trị bảng nội tuyến) với một vị từ rõ ràng cho tham số ngày / giờ.
- Đảm bảo rằng việc tạo số liệu thống kê tự động không bị điều chỉnh một cách không cần thiết
- Sử dụng đúng loại đối tượng tạm thời cho tác vụ (bảng so với biến)
- Xem xét
RECOMPILE
nếu lựa chọn gói rất nhạy cảm với các giá trị tham số
- Thêm
UPDATE STATISTICS
và RECOMPILE
nếu thống kê bộ nhớ cache là một vấn đề
- Hãy xem xét một bảng tạm thời với khóa chính thay vì
SELECT INTO
nếu nó cung cấp thông tin hữu ích cho trình tối ưu hóa
- Xem lại lược đồ để đảm bảo trình tối ưu hóa có nhiều thông tin nhất có thể (ví dụ: khóa ngoại, các ràng buộc khác)
- Xem xét sự phù hợp của các chỉ mục / thống kê được lọc dựa trên kiến thức về dữ liệu của bạn
- Đừng rắc
NOLOCK
gợi ý để tăng hiệu suất
Repro
Sau đây được xây dựng từ các thông tin hạn chế có sẵn trong các kế hoạch thực hiện được tái cấu trúc được cung cấp:
DROP VIEW IF EXISTS dbo.View_VP;
DROP TABLE IF EXISTS dbo.SmallTable, dbo.LargeTable, #MyTemp;
GO
CREATE TABLE LargeTable (P_Id integer NOT NULL, Status integer NOT NULL, Col1 integer NOT NULL, Col4 datetime NOT NULL);
CREATE TABLE SmallTable (P_id integer NOT NULL, Col2 integer NOT NULL)
CREATE TABLE #MyTemp (Col1 integer NOT NULL);
GO
CREATE VIEW dbo.View_VP
AS
SELECT
pp.Col1,
pd.Col2 AS Col2,
MAX(pp.Col4) AS Col3
FROM LargeTable pp
JOIN SmallTable pd
ON pd.P_id = pp.P_Id
WHERE
pp.[Status] IN (3, 4)
GROUP BY
pp.Col1, pd.Col2;
GO
CREATE UNIQUE CLUSTERED INDEX PK_SmallTable ON dbo.SmallTable (P_id)
CREATE CLUSTERED INDEX ix_P_id ON dbo.LargeTable (P_Id)
CREATE INDEX ix_Col1 ON dbo.LargeTable (Col1)
CREATE INDEX ix_Status ON dbo.LargeTable ([Status])
GO
UPDATE STATISTICS dbo.LargeTable WITH ROWCOUNT = 32268200, PAGECOUNT = 322682;
UPDATE STATISTICS dbo.SmallTable WITH ROWCOUNT = 6349, PAGECOUNT = 63;
UPDATE STATISTICS #MyTemp WITH ROWCOUNT = 4;
Truy vấn là:
DECLARE @Months integer = 6;
SELECT wd.Col1
, vp.Col2
, vp.Col3
FROM dbo.View_VP vp WITH (FORCESEEK)
INNER JOIN #MyTemp wd ON wd.Col1 = vp.Col1
WHERE vp.Col3 > DATEADD(MONTH, @Months * -1, GETDATE())
Không có số liệu thống kê thực tế trên các bảng cơ sở, điều này ủng hộ các kế hoạch gần với ví dụ 'kế hoạch xấu' (sử dụng ix_Status
):
Điều này cho thấy thông tin về tính chọn lọc Col1
là một yếu tố quan trọng trong lựa chọn của trình tối ưu hóa.