Cột tính toán liên tục gây ra quét


9

Chuyển đổi một cột thông thường thành một cột được tính toán bền bỉ đang khiến truy vấn này không thể thực hiện tìm kiếm chỉ mục. Tại sao?

Đã thử nghiệm trên một số phiên bản SQL Server, bao gồm 2016 SP1 CU1.

Repros

Vấn đề là với table1, col7.

Các bảng và truy vấn là một phiên bản một phần (và đơn giản hóa) của bản gốc. Tôi biết rằng truy vấn có thể được viết lại theo cách khác và vì một số lý do để tránh sự cố, nhưng chúng ta cần tránh chạm vào mã và câu hỏi tại sao table1không thể tìm kiếm vẫn đứng vững.

Như Paul White đã chỉ ra (cảm ơn!), Việc tìm kiếm có sẵn nếu bị ép buộc, vì vậy câu hỏi là: Tại sao việc tìm kiếm không được chọn bởi trình tối ưu hóa và liệu chúng ta có thể làm điều gì đó khác đi để thực hiện tìm kiếm hay không, mà không thay đổi mã?

Để làm rõ phần có vấn đề, đây là bản quét có liên quan trong kế hoạch thực hiện xấu:

kế hoạch

Câu trả lời:


12

Tại sao tìm kiếm không được chọn bởi trình tối ưu hóa


TL: DR Định nghĩa cột được tính toán mở rộng gây cản trở khả năng sắp xếp lại thứ tự tham gia ban đầu. Với một điểm khởi đầu khác, tối ưu hóa dựa trên chi phí sẽ đi một con đường khác thông qua trình tối ưu hóa và kết thúc với một lựa chọn kế hoạch cuối cùng khác.


Chi tiết

Đối với tất cả nhưng đơn giản nhất của các truy vấn, trình tối ưu hóa không cố gắng khám phá bất cứ điều gì như toàn bộ không gian của các kế hoạch có thể. Thay vào đó, nó chọn một điểm khởi đầu có vẻ hợp lý , sau đó dành một lượng nỗ lực ngân sách để khám phá các biến thể logic và vật lý, trong một hoặc nhiều giai đoạn tìm kiếm, cho đến khi tìm thấy một kế hoạch hợp lý.

Lý do chính bạn nhận được các kế hoạch khác nhau (với ước tính chi phí cuối cùng khác nhau) cho hai trường hợp là có điểm bắt đầu khác nhau . Bắt đầu từ một nơi khác, tối ưu hóa kết thúc ở một nơi khác (sau số lần lặp thăm dò và thực hiện hạn chế của nó). Tôi hy vọng điều này là hợp lý trực quan.

Điểm bắt đầu mà tôi đã đề cập, phần nào dựa trên biểu diễn văn bản của truy vấn, nhưng các thay đổi được thực hiện cho biểu diễn cây bên trong khi nó đi qua các giai đoạn phân tích cú pháp, ràng buộc, chuẩn hóa và đơn giản hóa của quá trình biên dịch truy vấn.

Điều quan trọng, điểm bắt đầu chính xác phụ thuộc rất nhiều vào thứ tự tham gia ban đầu được chọn bởi trình tối ưu hóa. Sự lựa chọn này được thực hiện trước khi số liệu thống kê được tải, và trước khi bất kỳ ước tính cardinality nào được đưa ra. Tuy nhiên, tổng số cardinality (số lượng hàng) trong mỗi bảng được lấy từ siêu dữ liệu hệ thống.

Do đó, thứ tự tham gia ban đầu là dựa trên heuristic . Ví dụ, trình tối ưu hóa cố gắng viết lại cây sao cho các bảng nhỏ hơn được nối trước các bảng lớn hơn và các phép nối bên trong xuất hiện trước các phép nối ngoài (và các phép nối chéo).

Sự hiện diện của cột được tính toán sẽ can thiệp vào quá trình này, đặc biệt là với khả năng của trình tối ưu hóa để đẩy các phép nối bên ngoài xuống cây truy vấn. Điều này là do cột được tính toán được mở rộng thành biểu thức cơ bản của nó trước khi sắp xếp lại tham gia và việc di chuyển một liên kết qua một biểu thức phức tạp khó khăn hơn nhiều so với việc di chuyển nó qua một tham chiếu cột đơn giản.

Các cây liên quan khá lớn, nhưng để minh họa, cây truy vấn ban đầu của cột không được tính toán bắt đầu bằng: (lưu ý hai phép nối ngoài ở trên cùng)

Đăng nhập
    LogOp_Apply (x_jtLeftOuter) 
        LogOp_LeftOuterJoin
            LogOp_NAryJoin
                LogOp_LeftAntiSemiJoin
                    LogOp_NAryJoin
                        LogOp_Get TBL: dbo.table1 (bí danh TBL: a4)
                        Đăng nhập
                            LogOp_Get TBL: dbo.table6 (bí danh TBL: a3)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL: [a3] .col18
                                ScaOp_Const TI (varar collate 53256, Var, Trim, ML = 16)
                        Đăng nhập
                            LogOp_Get TBL: dbo.table1 (bí danh TBL: a1)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL: [a1] .col2
                                ScaOp_Const TI (varar collate 53256, Var, Trim, ML = 16)
                        Đăng nhập
                            LogOp_Get TBL: dbo.table5 (bí danh TBL: a2)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL: [a2] .col2
                                ScaOp_Const TI (varar collate 53256, Var, Trim, ML = 16)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a4] .col2
                            ScaOp_Identifier QCOL: [a3] .col19
                    Đăng nhập
                        LogOp_Get TBL: dbo.table7 (bí danh TBL: a7)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a7] .col22
                            ScaOp_Const TI (varar collate 53256, Var, Trim, ML = 16)
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [a4] .col2
                        ScaOp_Identifier QCOL: [a7] .col23
                Đăng nhập
                    LogOp_Get TBL: bảng1 (bí danh TBL: cdc)
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [cdc] .col6
                        ScaOp_Const TI (smallint, ML = 2) XVAR (smallint, Không sở hữu, Giá trị = 4)
                LogOp_Get TBL: dbo.table5 (bí danh TBL: a5) 
                LogOp_Get TBL: bảng2 (bí danh TBL: cdt)  
                ScaOp_Logical x_lopAnd
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [a5] .col2
                        ScaOp_Identifier QCOL: [cdc] .col2
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [a4] .col2
                        ScaOp_Identifier QCOL: [cdc] .col2
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [cdt] .col1
                        ScaOp_Identifier QCOL: [cdc] .col1
            LogOp_Get TBL: bảng3 (bí danh TBL: ahcr)
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL: [ahcr] .col9
                ScaOp_Identifier QCOL: [cdt] .col1

Cùng một đoạn của truy vấn cột được tính là: (lưu ý phép nối ngoài thấp hơn nhiều, định nghĩa cột được tính mở rộng và một số khác biệt tinh tế khác trong thứ tự nối (bên trong)

Đăng nhập
    LogOp_Apply (x_jtLeftOuter)
        LogOp_NAryJoin
            LogOp_LeftAntiSemiJoin
                LogOp_NAryJoin
                    LogOp_Get TBL: dbo.table1 (bí danh TBL: a4)
                    Đăng nhập
                        LogOp_Get TBL: dbo.table6 (bí danh TBL: a3)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a3] .col18
                            ScaOp_Const TI (varar collate 53256, Var, Trim, ML = 16)
                    Đăng nhập
                        LogOp_Get TBL: dbo.table1 (bí danh TBL: a1
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a1] .col2
                            ScaOp_Const TI (varar collate 53256, Var, Trim, ML = 16)
                    Đăng nhập
                        LogOp_Get TBL: dbo.table5 (bí danh TBL: a2)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a2] .col2
                            ScaOp_Const TI (varar collate 53256, Var, Trim, ML = 16)
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [a4] .col2
                        ScaOp_Identifier QCOL: [a3] .col19
                Đăng nhập
                    LogOp_Get TBL: dbo.table7 (bí danh TBL: a7) 
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [a7] .col22
                        ScaOp_Const TI (varar collate 53256, Var, Trim, ML = 16)
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL: [a4] .col2
                    ScaOp_Identifier QCOL: [a7] .col23
            LogOp_Project
                LogOp_LeftOuterJoin
                    Đăng nhập
                        Đăng nhập
                            LogOp_Get TBL: bảng1 (bí danh TBL: cdc) 
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL: [cdc] .col6
                                ScaOp_Const TI (smallint, ML = 2) XVAR (smallint, Không sở hữu, Giá trị = 4)
                        LogOp_Get TBL: bảng2 (bí danh TBL: cdt) 
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [cdc] .col1
                            ScaOp_Identifier QCOL: [cdt] .col1
                    LogOp_Get TBL: bảng3 (bí danh TBL: ahcr) 
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [ahcr] .col9
                        ScaOp_Identifier QCOL: [cdt] .col1
                AncOp_PrjList 
                    AncOp_PrjEl QCOL: [cdc] .col7
                        ScaOp_Convert char collate 53256, Null, Trim, ML = 6
                            ScaOp_IIF varar đối chiếu 53256, Null, Var, Trim, ML = 6
                                ScaOp_Comp x_cmpEq
                                    ScaOp_Intrinsic isnumeric
                                        ScaOp_Intrinsic phải
                                            ScaOp_Identifier QCOL: [cdc] .col4
                                            ScaOp_Const TI (int, ML = 4) XVAR (int, Không sở hữu, Giá trị = 4)
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, Không sở hữu, Giá trị = 0)
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 1) XVAR (varchar, Owned, Value = Len, Data = (0,))
                                Chuỗi con ScaOp_Intrinsic
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, Không sở hữu, Giá trị = 6)
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, Không sở hữu, Giá trị = 1)
                                    ScaOp_Identifier QCOL: [cdc] .col4
            LogOp_Get TBL: dbo.table5 (bí danh TBL: a5)
            ScaOp_Logical x_lopAnd
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL: [a5] .col2
                    ScaOp_Identifier QCOL: [cdc] .col2
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL: [a4] .col2
                    ScaOp_Identifier QCOL: [cdc] .col2

Số liệu thống kê được tải và ước tính cardinality ban đầu được thực hiện trên cây ngay sau khi thứ tự tham gia ban đầu được thiết lập. Việc tham gia vào các đơn đặt hàng khác nhau cũng ảnh hưởng đến các ước tính này và do đó có tác dụng gõ cửa trong quá trình tối ưu hóa dựa trên chi phí sau này.

Cuối cùng, đối với phần này, việc nối bên ngoài bị kẹt ở giữa cây có thể ngăn chặn một số quy tắc sắp xếp lại tham gia phù hợp trong quá trình tối ưu hóa dựa trên chi phí.


Sử dụng hướng dẫn kế hoạch (hoặc, tương đương là một USE PLANgợi ý - ví dụ cho truy vấn của bạn ) sẽ thay đổi chiến lược tìm kiếm thành cách tiếp cận hướng mục tiêu hơn, được hướng dẫn bởi hình dạng và tính năng chung của mẫu được cung cấp. Điều này giải thích tại sao trình tối ưu hóa có thể tìm thấy cùng một table1kế hoạch tìm kiếm đối với cả lược đồ cột được tính toán và không tính toán, khi hướng dẫn kế hoạch hoặc gợi ý được sử dụng.

Liệu chúng ta có thể làm điều gì đó khác đi để thực hiện tìm kiếm

Đây là điều bạn chỉ cần lo lắng nếu trình tối ưu hóa không tìm thấy kế hoạch với các đặc tính hiệu suất có thể chấp nhận được.

Tất cả các công cụ điều chỉnh bình thường có khả năng áp dụng. Ví dụ, bạn có thể chia truy vấn thành các phần đơn giản hơn, xem xét và cải thiện việc lập chỉ mục có sẵn, cập nhật hoặc tạo số liệu thống kê mới ... vv.

Tất cả những điều này có thể ảnh hưởng đến ước tính cardinality, đường dẫn mã được thực hiện thông qua trình tối ưu hóa và ảnh hưởng đến các quyết định dựa trên chi phí theo những cách tinh tế.

Cuối cùng, bạn có thể sử dụng các gợi ý (hoặc hướng dẫn kế hoạch), nhưng đó thường không phải là giải pháp lý tưởng.


Câu hỏi bổ sung từ ý kiến

Tôi đồng ý rằng tốt nhất là đơn giản hóa truy vấn, v.v., nhưng có cách nào (cờ theo dõi) để làm cho trình tối ưu hóa tiếp tục với tối ưu hóa và đạt được kết quả tương tự không?

Không, không có cờ theo dõi để thực hiện tìm kiếm toàn diện và bạn không muốn một tìm kiếm. Không gian tìm kiếm có thể là rất lớn và thời gian biên soạn vượt quá tuổi của vũ trụ sẽ không được đón nhận. Ngoài ra, trình tối ưu hóa không biết mọi biến đổi logic có thể có (không ai thực hiện).

Ngoài ra, tại sao việc mở rộng phức tạp cần thiết, vì cột vẫn tồn tại? Tại sao trình tối ưu hóa không thể tránh mở rộng nó, coi nó như một cột thông thường và đạt đến điểm khởi đầu giống nhau?

Các cột được tính toán được mở rộng (như chế độ xem) để cho phép các cơ hội tối ưu hóa bổ sung. Việc mở rộng có thể được khớp trở lại, ví dụ như một cột hoặc chỉ mục được duy trì sau này trong quá trình, nhưng điều này xảy ra sau khi thứ tự tham gia ban đầu được cố định.

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.