Thay đổi về ước tính trên các vị từ có chứa SUBSTRING () trong SQL Server 2016?


13

Có tài liệu hay nghiên cứu nào về các thay đổi trong SQL Server 2016 về cách ước tính cardinality cho các vị từ có chứa SUBSTRING () hoặc các hàm chuỗi khác không?

Lý do tôi hỏi là tôi đã xem xét một truy vấn có hiệu suất bị suy giảm trong chế độ tương thích 130 và lý do có liên quan đến sự thay đổi trong ước tính số lượng hàng khớp với mệnh đề WHERE có lệnh gọi SUBSTRING (). Tôi đã khắc phục sự cố bằng cách viết lại truy vấn, nhưng tôi tự hỏi liệu có ai biết bất kỳ tài liệu nào về các thay đổi trong lĩnh vực này trong SQL Server 2016 không.

Mã demo dưới đây. Các ước tính rất gần trong trường hợp thử nghiệm này, nhưng độ chính xác khác nhau tùy thuộc vào dữ liệu.

Trong trường hợp thử nghiệm, ở cấp độ compat 120, SQL Server dường như đang sử dụng biểu đồ cho ước tính, trong khi ở mức độ tương thích, 130 Server SQL dường như giả định 10% cố định của bảng khớp.

CREATE DATABASE MyStringTestDB;
GO
USE MyStringTestDB;
GO
DROP TABLE IF EXISTS dbo.StringTest;
CREATE TABLE dbo.StringTest ( [TheString] varchar(15) );
GO
INSERT INTO dbo.StringTest
VALUES
( 'Y5_CLV' );
INSERT INTO dbo.StringTest
VALUES
( 'Y5_EG3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_NE' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_PQT' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_T2V' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_TT4' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_ZKK' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_LW6' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_QO3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_TZ7' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_UZZ' );

CREATE CLUSTERED INDEX IX_Clustered ON dbo.StringTest (TheString);

/* 
Uses fixed % for estimate; 1.1 rows estimated in this case.
    Plan for computation:
        CSelCalcFixedFilter (0.1) <----
            Selectivity: 0.1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 130;
GO
SELECT * 
FROM dbo.StringTest 
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);

/* 
Uses histogram to get estimate of 1
 CSelCalcPointPredsFreqBased <----
      Distinct value calculation:
          CDVCPlanLeaf
              0 Multi-Column Stats, 1 Single-Column Stats, 0 Guesses
      Individual selectivity calculations:
          (none)
    Loaded histogram for column QCOL: [DBA].[dbo].[StringTest].TheString from stats with id 1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 120;
GO
SELECT * 
FROM dbo.StringTest 
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);

/*
-- Simpler rewrite; works fine in both compat levels and gets better estimate.
SELECT * 
FROM dbo.StringTest 
WHERE TheString LIKE 'ZZ[_]%'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);
*/

1
Không chắc chắn về câu hỏi cụ thể, nhưng nếu các Y5_EG3chuỗi chỉ là mã và luôn luôn viết hoa, thì bạn luôn có thể thử chỉ định đối chiếu nhị phân - Latin1_General_100_BIN2- điều này sẽ cải thiện tốc độ của các hoạt động lọc. Chỉ cần thêm COLLATE Latin1_General_100_BIN2vào các CREATE TABLEtuyên bố, ngay sau khi varchar(15). Tôi sẽ tò mò xem liệu nó cũng ảnh hưởng đến việc tạo / ước tính kế hoạch.
Solomon Rutzky

Câu trả lời:


8

Tôi không biết về bất kỳ tài liệu nào. Tôi đã xem xét điều này và đưa ra một số quan sát tuy nhiên quá dài cho một nhận xét.

Ước tính 10% không phải lúc nào cũng là một sự xuống cấp. Lấy ví dụ sau.

TRUNCATE TABLE dbo.StringTest

INSERT INTO dbo.StringTest
SELECT TOP (1000000) 'ZZ_' + LEFT(NEWID(), 12)
FROM   master..spt_values v1,
       master..spt_values v2;

WHEREmệnh đề trong câu hỏi của bạn.

WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'

Bảng chứa một triệu hàng. Tất cả đều khớp với vị ngữ. Dưới mức độ tương thích 130, dự đoán 10% mang lại ước tính 100.000. Dưới 120 hàng ước tính là 1.03913.

Hành vi 120 sử dụng biểu đồ nhưng chỉ để có được số lượng hàng khác nhau. Vectơ mật độ trong trường hợp của tôi hiển thị 1.039131E-06 và giá trị này được nhân với số lượng thẻ bảng để có được số lượng hàng ước tính. Tất cả các giá trị trên thực tế là khác nhau nhưng tất cả đều khớp với vị ngữ.

Theo dõi query_optimizer_estimate_cardinalitysự kiện mở rộng cho thấy dưới 130 có hai <StatsCollection Name="CStCollFilter"sự kiện khác nhau . Người đầu tiên ước tính 100.000. Cái thứ hai tải biểu đồ và sử dụng CSelCalcPointPredsFreqBasing / DistincCountCalculator để lấy ước tính 1.04. Kết quả thứ hai này xuất hiện không được sử dụng.

Hành vi mà bạn quan sát không được áp dụng nhất quán trong 130. Tôi đã thêm ORDER BY TheStringhy vọng đây sẽ là một chiến thắng rõ ràng cho người ước tính 130 vì 120 đấu tranh với việc cấp bộ nhớ cho một hàng nhưng thay đổi nhỏ này là đủ để đưa các hàng ước tính xuống 1.03913 trong trường hợp 130 quá.

Việc thêm hoàn OPTION (QUERYRULEOFF SelectToFilter)nguyên ước tính sắp xếp thành 100.000 nhưng cấp bộ nhớ không tăng và các ước tính sắp xếp vẫn dựa trên các giá trị riêng biệt của bảng.

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

Tương tự điều chỉnh ngưỡng chi phí cho tính song song để truy vấn có được kế hoạch song song là đủ trong trường hợp 130 để trở lại ước tính thấp hơn. Thêm QUERYTRACEON 8757cũng gây ra ước tính thấp hơn. Có vẻ như ước tính 10% chỉ được giữ lại cho các kế hoạch tầm thường.

Viết lại đề xuất của bạn với

WHERE TheString LIKE 'ZZ[_]%'

Cho thấy nhiều ước tính vượt trội cho cả hai. Đầu ra cho điều này là

  CSelCalcTrieBased

      Column: QCOL: [MyStringTestDB].[dbo].[StringTest].TheString

Cho thấy rằng nó đã sử dụng thử . Thông tin thêm về điều này nằm trong phần thống kê tóm tắt chuỗi ngay phía trên đây .

Nó không giống như truy vấn ban đầu của bạn. Vì phiên bản đầu tiên _hiện được coi là luôn là ký tự thứ ba thay vì được tìm thấy một cách linh hoạt.

Nếu giả định này được mã hóa vào truy vấn ban đầu của bạn

 WHERE SUBSTRING(TheString, 1, 3) = 'ZZ_'

Phương pháp ước tính thay đổi thành CSelCalcHistogramComparison(INTERVAL)và các hàng ước tính trở nên chính xác.

Nó có thể chuyển đổi nó thành một phạm vi

WHERE TheString >=  'ZZ_' AND TheString < ???

và sử dụng biểu đồ để ước tính số lượng hàng có giá trị trong phạm vi đó.

Điều này chỉ áp dụng cho ước tính cardinality tuy nhiên. LIKElà tốt hơn vì nó có thể sử dụng một phạm vi tìm kiếm trong thời gian chạy. SUBSTRING(TheString, 1, 3)hoặc LEFT(TheString, 3)không thể.

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.