Các hàm cửa sổ gây ra kế hoạch thực hiện khủng khiếp khi được gọi từ một khung nhìn với mệnh đề 'tham số' bên ngoài


10

Tôi đã có vấn đề này từ lâu, tôi đã tìm thấy một cách giải quyết phù hợp với tôi và quên nó đi.

Nhưng bây giờ có câu hỏi về SO vì vậy tôi sẵn sàng đưa vấn đề này lên.

Có một khung nhìn tham gia vài bảng theo cách rất đơn giản (đơn hàng + dòng đơn hàng).

Khi được truy vấn mà không có một wheremệnh đề, khung nhìn trả về vài triệu dòng.
Tuy nhiên, không ai từng gọi nó như thế. Truy vấn thông thường là

select * from that_nasty_view where order_number = 123456;

Điều này trả về khoảng 10 hồ sơ trong số 5m.

Một điều quan trọng: chế độ xem chứa chức năng cửa sổ rank(), được phân vùng chính xác theo trường sử dụng chế độ xem luôn được truy vấn:

rank() over (partition by order_number order by detail_line_number)

Bây giờ, nếu chế độ xem này được truy vấn với các tham số bằng chữ trong chuỗi truy vấn, chính xác như được hiển thị ở trên, nó sẽ trả về các hàng ngay lập tức. Kế hoạch thực hiện là tốt:

  • Chỉ mục tìm kiếm trên cả hai bảng bằng cách sử dụng các chỉ số trên order_number(trả về 10 hàng).
  • Tính toán các cửa sổ trên kết quả nhỏ được trả về.
  • Lựa chọn.

Tuy nhiên, khi chế độ xem được gọi theo cách tối ưu, mọi thứ trở nên khó chịu:

  • Index scantrên tất cả các bảng bỏ qua các chỉ số. Trả về 5m hàng.
  • Tham gia rất lớn.
  • Tính toán các cửa sổ trên tất cả partitions (khoảng 500k cửa sổ).
  • Filter để lấy 10 hàng trong số 5m.
  • Lựa chọn

Điều này xảy ra trong mọi trường hợp khi các tham số có liên quan. Nó có thể là SSMS:

declare @order_number int = 123456;
select * from that_nasty_view where order_number = @order_number;

Nó có thể là máy khách ODBC, chẳng hạn như Excel:

select * from that_nasty_view where order_number = ?

Hoặc nó có thể là bất kỳ máy khách nào khác sử dụng tham số và không ghép nối sql.

Nếu chức năng cửa sổ được loại bỏ khỏi chế độ xem, nó sẽ chạy hoàn toàn nhanh chóng, bất kể nó có được truy vấn hay không.

Cách giải quyết của tôi là loại bỏ chức năng vi phạm và áp dụng lại ở giai đoạn sau.

Nhưng, những gì cho? Đây có thực sự là một lỗi trong cách SQL Server 2008 xử lý các chức năng của cửa sổ không?


order_number là khóa chính? Kiểu dữ liệu của cột và tham số khớp?
gbn

order_numberkhông phải là khóa chính. Đó là int not nullvới chỉ mục không bao gồm trên nó trong cả hai bảng.
GSerg

5
SQL Server 2005 có vấn đề với việc đẩy vị ngữ trong lĩnh vực này. Tôi nghĩ rằng họ đã được sửa chữa ngay bây giờ. BTW ví dụ TSQL của bạn sử dụng một biến không phải là một tham số. Có thêm OPTION (RECOMPILE)trợ giúp?
Martin Smith

1
@GSerg - Vậy trong kế hoạch xấu với bộ lọc cuối cùng, nó có khoảng 5 triệu hàng vào bộ lọc và ước tính có 10 hàng phù hợp với thực tế? Nếu vậy có lẽ đó là vấn đề đẩy vị ngữ vẫn chưa được khắc phục hoàn toàn sau đó.
Martin Smith

Câu trả lời:


5

Đây dường như là một vấn đề tồn tại lâu dài, tiếp tục xuất hiện trở lại ở dạng này hay dạng khác và vẫn còn hiện diện trong SQL Server 2012.

Một số bài viết thảo luận về nó là

Tất cả các phiên bản hiện tại của SQL Server cho đến và bao gồm cả năm 2012 đều không thể đẩy bộ lọc trên một nhóm phân vùng qua dự án chuỗi cho một vị từ được tham số hóa trừ khi option(recompile)được sử dụng (nếu 2008+).

Một thay thế cho recompilegợi ý sẽ là viết lại truy vấn để sử dụng TVF nội tuyến được tham số hóa theo đề xuất của @ a1ex07)


Cũng có trường hợp trong SQL Server 2014
Guillaume86

3

Tôi sẽ thử thay thế chế độ xem bằng udf có giá trị bảng. Bằng cách đó, nó sẽ lọc các bản ghi đầu tiên, và sau đó áp dụng chức năng cửa sổ. Hàm này có thể chấp nhận tham số bảng để bạn có thể truyền nhiều order_numbervào nó


Một cách giải quyết khác, vâng. Tôi không thể làm điều đó mặc dù vì không phải tất cả các khách hàng đều có thể sử dụng hàm có giá trị bảng.
GSerg

Tại sao? Tôi không chắc chắn 100%, nhưng tôi nghĩ tất cả những gì bạn cần là thay đổi truy vấn một chút thành một cái gì đó nhưSELECT * FROM my_funct(12345)
a1ex07

Một trong những yêu cầu là truy vấn có thể sử dụng được bởi người dùng cuối sử dụng Excel (nghĩa là bằng MS Query) và MS Query sẽ không cho phép bạn làm điều đó, ít nhất là trong các phiên bản cho đến năm 2003.
GSerg

it will filter records first, and then apply window functionkhông chính xác Không có lệnh quyết định để thực hiện
Remus Rusanu
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.