SQL Server 2014: bất kỳ lời giải thích nào cho ước tính cardinality không nhất quán?


27

Hãy xem xét kế hoạch truy vấn sau trong SQL Server 2014:

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

Trong kế hoạch truy vấn, tự tham gia ar.fId = ar.fIdmang lại ước tính 1 hàng. Tuy nhiên, đây là một ước tính không nhất quán về mặt logic: ar20,608các hàng và chỉ một giá trị riêng biệt của fId(được phản ánh chính xác trong các thống kê). Do đó, phép nối này tạo ra sản phẩm chéo đầy đủ của các hàng ( ~424MMhàng), khiến truy vấn chạy trong vài giờ.

Tôi đang gặp khó khăn trong việc hiểu tại sao SQL Server sẽ đưa ra một ước tính có thể dễ dàng được chứng minh là không phù hợp với số liệu thống kê. Có ý kiến ​​gì không?

Điều tra ban đầu và chi tiết bổ sung

Dựa trên câu trả lời của Paul ở đây , có vẻ như cả hai phương pháp phỏng đoán SQL 2012 và SQL 2014 để ước tính số lượng thẻ tham gia sẽ dễ dàng xử lý một tình huống cần so sánh hai biểu đồ giống hệt nhau.

Tôi đã bắt đầu với đầu ra từ cờ theo dõi 2363, nhưng không thể hiểu điều đó một cách dễ dàng. Đoạn mã sau có nghĩa là Máy chủ SQL đang so sánh biểu đồ cho fIdbIdđể ước tính tính chọn lọc của phép nối chỉ sử dụng fIdkhông? Nếu vậy, điều đó rõ ràng là không chính xác. Hoặc tôi đang đọc sai sản lượng cờ theo dõi?

Plan for computation:
  CSelCalcExpressionComparedToExpression( QCOL: [ar].fId x_cmpEq QCOL: [ar].fId )
Loaded histogram for column QCOL: [ar].bId from stats with id 3
Loaded histogram for column QCOL: [ar].fId from stats with id 1
Selectivity: 0

Lưu ý rằng tôi đã đưa ra một số cách giải quyết, được bao gồm trong tập lệnh repro đầy đủ và đưa truy vấn này xuống đến mili giây. Câu hỏi này tập trung vào việc tìm hiểu hành vi, cách tránh nó trong các truy vấn trong tương lai và xác định xem đó có phải là lỗi cần phải nộp cho Microsoft hay không.

Đây là tập lệnh repro đầy đủ , đây là đầu ra đầy đủ từ cờ theo dõi 2363 và đây là định nghĩa bảng truy vấn và bảng trong trường hợp bạn muốn xem nhanh chúng mà không cần mở tập lệnh đầy đủ:

WITH cte AS (
    SELECT ar.fId, 
        ar.bId,
        MIN(CONVERT(INT, ar.isT)) AS isT,
        MAX(CONVERT(INT, tcr.isS)) AS isS
    FROM  #SQL2014MinMaxAggregateCardinalityBug_ar ar 
    LEFT OUTER JOIN #SQL2014MinMaxAggregateCardinalityBug_tcr tcr
        ON tcr.rId = 508
        AND tcr.fId = ar.fId
        AND tcr.bId = ar.bId
    GROUP BY ar.fId, ar.bId
)
SELECT s.fId, s.bId, s.isS, t.isS
FROM cte s 
JOIN cte t 
    ON t.fId = s.fId 
    AND t.isT = 1

CREATE TABLE #SQL2014MinMaxAggregateCardinalityBug_ar (
    fId INT NOT NULL,
    bId INT NOT NULL,
    isT BIT NOT NULL
    PRIMARY KEY (fId, bId)
)

CREATE TABLE #SQL2014MinMaxAggregateCardinalityBug_tcr (
    rId INT NOT NULL,
    fId INT NOT NULL,
    bId INT NOT NULL,
    isS BIT NOT NULL
    PRIMARY KEY (rId, fId, bId, isS)
)

Câu trả lời:


23

Tôi đang gặp khó khăn trong việc hiểu tại sao SQL Server sẽ đưa ra một ước tính có thể dễ dàng được chứng minh là không phù hợp với số liệu thống kê.

Tính nhất quán

Không có sự đảm bảo chung về tính nhất quán. Ước tính có thể được tính trên các cây con khác nhau (nhưng tương đương về mặt logic) tại các thời điểm khác nhau, sử dụng các phương pháp thống kê khác nhau.

Không có gì sai với logic nói rằng việc tham gia hai cây con giống hệt nhau phải tạo ra một sản phẩm chéo, nhưng cũng không có gì để nói rằng sự lựa chọn lý luận là âm thanh hơn bất kỳ thứ gì khác.

Dự toán ban đầu

Trong trường hợp cụ thể của bạn, ước tính cardinality ban đầu cho phép nối không được thực hiện trên hai cây con giống hệt nhau . Hình dạng cây lúc đó là:

  Đăng nhập
     LogOp_GbAgg
        LogOp_LeftOuterJoin
           LogOp_Get TBL: ar
           Đăng nhập
              LogOp_Get TBL: tcr
              ScaOp_Comp x_cmpEq
                 ScaOp_Identifier [tcr] .rId
                 Giá trị ScaOp_Const = 508
           ScaOp_Logical x_lopAnd
              ScaOp_Comp x_cmpEq
                 ScaOp_Identifier [ar] .fId
                 ScaOp_Identifier [tcr] .fId
              ScaOp_Comp x_cmpEq
                 ScaOp_Identifier [ar] .bId
                 ScaOp_Identifier [tcr] .bId
        AncOp_PrjList 
           AncOp_PrjEl Expr1003 
              ScaOp_AggFunc stopMax
                 ScaOp_Convert int
                    ScaOp_Identifier [tcr] .isS
     Đăng nhập
        LogOp_GbAgg
           LogOp_LeftOuterJoin
              LogOp_Get TBL: ar
              Đăng nhập
                 LogOp_Get TBL: tcr
                 ScaOp_Comp x_cmpEq
                    ScaOp_Identifier [tcr] .rId
                    Giá trị ScaOp_Const = 508
              ScaOp_Logical x_lopAnd
                 ScaOp_Comp x_cmpEq
                    ScaOp_Identifier [ar] .fId
                    ScaOp_Identifier [tcr] .fId
                 ScaOp_Comp x_cmpEq
                    ScaOp_Identifier [ar] .bId
                    ScaOp_Identifier [tcr] .bId
           AncOp_PrjList 
              AncOp_PrjEl Expr1006 
                 ScaOp_AggFunc stopMin
                    ScaOp_Convert int
                       ScaOp_Identifier [ar] .isT
              AncOp_PrjEl Expr1007 
                 ScaOp_AggFunc stopMax
                    ScaOp_Convert int
                       ScaOp_Identifier [tcr] .isS
        ScaOp_Comp x_cmpEq
           ScaOp_Identifier Expr1006 
           Giá trị ScaOp_Const = 1
     ScaOp_Comp x_cmpEq
        ScaOp_Identifier QCOL: [ar] .fId
        ScaOp_Identifier QCOL: [ar] .fId

Là người đầu tiên tham gia đầu vào đã có một tổng unprojected đơn giản hóa đi, và lần thứ hai tham gia vào có vị t.isT = 1đẩy dưới nó, nơi t.isTMIN(CONVERT(INT, ar.isT)). Mặc dù vậy, tính toán chọn lọc cho isTvị từ có thể sử dụng CSelCalcColumnInIntervaltrên biểu đồ:

  CSelCalcColumnInInterval
      Cột: COL: Expr1006 

Biểu đồ được tải cho cột QCOL: [ar] .isT từ số liệu thống kê với id 3

Độ chọn lọc: 4.85248e-005

Bộ sưu tập số liệu thống kê được tạo: 
  CStCollFilter (ID = 11, CARD = 1)
      CStCollgroupBy (ID = 10, CARD = 20608)
          CStCollOuterJoin (ID = 9, CARD = 20608 x_jtLeftOuter)
              CStCollBaseTable (ID = 3, CARD = 20608 TBL: ar)
              CStCollFilter (ID = 8, CARD = 1)
                  CStCollBaseTable (ID = 4, CARD = 28 TBL: tcr)

Kỳ vọng (chính xác) là cho 20.608 hàng được giảm xuống 1 hàng theo vị từ này.

Tham gia dự toán

Câu hỏi bây giờ trở thành cách 20.608 hàng từ đầu vào tham gia khác sẽ khớp với một hàng này:

  Đăng nhập
      CStCollgroupBy (ID = 7, CARD = 20608)
          CStCollOuterJoin (ID = 6, CARD = 20608 x_jtLeftOuter)
              ...

      CStCollFilter (ID = 11, CARD = 1)
          CStCollgroupBy (ID = 10, CARD = 20608)
              ...

      ScaOp_Comp x_cmpEq
          ScaOp_Identifier QCOL: [ar] .fId
          ScaOp_Identifier QCOL: [ar] .fId

Có một số cách khác nhau để ước tính sự tham gia nói chung. Chúng ta có thể, ví dụ:

  • Lấy biểu đồ mới tại mỗi toán tử kế hoạch trong mỗi cây con, căn chỉnh chúng tại liên kết (nội suy các giá trị bước khi cần thiết) và xem chúng khớp với nhau như thế nào; hoặc là
  • Thực hiện căn chỉnh biểu đồ 'thô' đơn giản hơn (sử dụng các giá trị tối thiểu và tối đa, không phải từng bước); hoặc là
  • Tính toán các lựa chọn riêng biệt cho các cột tham gia (từ bảng cơ sở và không có bất kỳ bộ lọc nào), sau đó thêm vào hiệu ứng chọn lọc của (các) vị từ không tham gia.
  • ...

Tùy thuộc vào công cụ ước tính cardinality đang sử dụng và một số phương pháp phỏng đoán, bất kỳ trong số đó (hoặc một biến thể) có thể được sử dụng. Xem Sách trắng của Microsoft Tối ưu hóa các gói truy vấn của bạn với Công cụ ước tính số lượng thẻ SQL Server 2014 để biết thêm.

Lỗi?

Bây giờ, như đã lưu ý trong câu hỏi, trong trường hợp này, phép nối đơn cột 'đơn giản' (bật fId) sử dụng CSelCalcExpressionComparedToExpressionmáy tính:

Kế hoạch tính toán:

  CSelCalcExpressionComparedToExpression [ar] .fId x_cmpEq [ar] .fId

Biểu đồ được tải cho cột QCOL: [ar] .bId từ số liệu thống kê với id 2
Biểu đồ được tải cho cột QCOL: [ar] .fId từ số liệu thống kê với id 1

Độ chọn lọc: 0

Tính toán này đánh giá rằng việc tham gia 20.608 hàng với 1 hàng được lọc sẽ có độ chọn lọc bằng 0: không có hàng nào khớp (được báo cáo là một hàng trong các kế hoạch cuối cùng). Điều này có sai không? Vâng, có lẽ có một lỗi trong CE mới ở đây. Người ta có thể lập luận rằng 1 hàng sẽ khớp với tất cả các hàng hoặc không có hàng, vì vậy kết quả có thể hợp lý, nhưng có lý do để tin khác.

Các chi tiết thực sự khá khó khăn, nhưng kỳ vọng ước tính sẽ dựa trên fIdbiểu đồ chưa được lọc , được sửa đổi bởi tính chọn lọc của bộ lọc, đưa ra 20608 * 20608 * 4.85248e-005 = 20608các hàng là rất hợp lý.

Theo cách tính này có nghĩa là sử dụng máy tính CSelCalcSimpleJoinWithDistinctCountsthay vì CSelCalcExpressionComparedToExpression. Không có cách nào để làm điều này, nhưng nếu bạn tò mò, bạn có thể bật cờ theo dõi không có giấy tờ 9479:

Kế hoạch 9479

Lưu ý phép nối cuối cùng tạo ra 20.608 hàng từ hai đầu vào một hàng, nhưng điều đó không gây ngạc nhiên. Đó là kế hoạch tương tự được sản xuất bởi CE ban đầu theo TF 9481.

Tôi đã đề cập đến các chi tiết rất khó (và tốn thời gian để điều tra), nhưng theo như tôi có thể nói, nguyên nhân gốc rễ của vấn đề có liên quan đến vị ngữ rId = 508, với độ chọn lọc bằng không. Ước tính không này được nâng lên thành một hàng theo cách thông thường, dường như đóng góp cho ước tính chọn lọc bằng 0 tại câu hỏi khi nó chiếm các vị từ thấp hơn trong cây đầu vào (do đó tải số liệu thống kê cho bId).

Cho phép tham gia bên ngoài để giữ ước tính bên trong không có hàng (thay vì tăng lên một hàng) (vì vậy tất cả các hàng bên ngoài đủ điều kiện) sẽ đưa ra ước tính tham gia 'không có lỗi' với máy tính. Nếu bạn thích khám phá điều này, cờ theo dõi không có giấy tờ là 9473 (một mình):

Kế hoạch 9473

Hành vi của ước tính cardinality tham gia CSelCalcExpressionComparedToExpressioncũng có thể được sửa đổi để không tính đến `` bId` với một cờ biến thể không có giấy tờ khác (9494). Tôi đề cập đến tất cả những điều này bởi vì tôi biết bạn có hứng thú với những điều như vậy; không phải vì họ đưa ra một giải pháp. Cho đến khi bạn báo cáo vấn đề với Microsoft và họ giải quyết vấn đề đó (hoặc không), thể hiện truy vấn khác nhau có lẽ là cách tốt nhất để chuyển tiếp. Bất kể hành vi đó là cố ý hay không, họ nên quan tâm để nghe về hồi quy.

Cuối cùng, để thu dọn một thứ khác được đề cập trong kịch bản sao chép: vị trí cuối cùng của Bộ lọc trong kế hoạch câu hỏi là kết quả của một cuộc thăm dò dựa trên chi phí GbAggAfterJoinSeldi chuyển tổng hợp và bộ lọc lên trên phép nối, vì đầu ra nối có nhỏ như vậy số hàng. Bộ lọc ban đầu bên dưới tham gia, như bạn mong đợi.

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.