การเปลี่ยนแปลงค่าประมาณของเพรดิเคตที่มี SUBSTRING () ใน SQL Server 2016 หรือไม่


13

มีเอกสารหรืองานวิจัยใดเกี่ยวกับการเปลี่ยนแปลงใน SQL Server 2016 ถึงความคาดการณ์ของ cardinality สำหรับเพรดิเคตที่มี SUBSTRING () หรือฟังก์ชันสตริงอื่น ๆ หรือไม่?

เหตุผลที่ฉันถามคือฉันกำลังดูคิวรีที่ประสิทธิภาพลดลงในโหมดความเข้ากันได้ 130 และสาเหตุที่เกี่ยวข้องกับการเปลี่ยนแปลงในการประมาณจำนวนแถวที่ตรงกับส่วนคำสั่ง WHERE ที่มีการเรียกไปยัง SUBSTRING () ฉันแก้ไขปัญหาด้วยการเขียนแบบสอบถามใหม่ แต่สงสัยว่าถ้าใครรู้เรื่องเอกสารเกี่ยวกับการเปลี่ยนแปลงในพื้นที่นี้ใน SQL Server 2016

รหัสการสาธิตอยู่ด้านล่าง ค่าประมาณใกล้เคียงกันมากในกรณีทดสอบนี้ แต่ความแม่นยำนั้นขึ้นอยู่กับข้อมูล

ในกรณีทดสอบในระดับที่เข้ากันได้ 120, SQL Server ดูเหมือนจะใช้ฮิสโตแกรมสำหรับการประมาณการในขณะที่ในระดับที่เข้ากันได้ 130 SQL Server ดูเหมือนจะสมมติว่า 10% คงที่ของตารางที่ตรงกัน

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
ไม่แน่ใจเกี่ยวกับคำถามเฉพาะ แต่ถ้าY5_EG3สตริงเป็นเพียงรหัสและตัวพิมพ์ใหญ่คุณสามารถลองระบุการเปรียบเทียบไบนารีLatin1_General_100_BIN2- ซึ่งควรปรับปรุงความเร็วในการกรอง เพียงแค่เพิ่มCOLLATE Latin1_General_100_BIN2ไปยังคำสั่งหลังCREATE TABLE varchar(15)ฉันอยากรู้ว่ามันจะส่งผลกระทบต่อการสร้าง / การประมาณแผนหรือไม่
โซโลมอน Rutzky

ขอให้เรายังคงอภิปรายนี้ในการแชท
James L

คำตอบ:


8

ฉันไม่ได้ตระหนักถึงเอกสารใด ๆ ฉันลองดูสิ่งนี้และทำการสังเกตบางอย่าง แต่นั่นยาวเกินไปสำหรับความคิดเห็น

ค่าประมาณ 10% นั้นไม่ได้เป็นการลดลงเสมอไป นำตัวอย่างต่อไปนี้

TRUNCATE TABLE dbo.StringTest

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

และWHEREประโยคในคำถามของคุณ

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

ตารางประกอบด้วยหนึ่งล้านแถว พวกเขาทั้งหมดตรงกับคำกริยา ภายใต้ระดับที่เข้ากันได้ 130 การคาดเดา 10% ให้ผลประมาณ 100,000 ต่ำกว่า 120 แถวโดยประมาณคือ 1.03913

พฤติกรรม 120 ใช้ฮิสโตแกรม แต่จะได้รับจำนวนแถวที่แตกต่างเท่านั้น เวกเตอร์ความหนาแน่นในกรณีของฉันแสดง 1.039131E-06 และนี่จะถูกคูณด้วยความสำคัญเชิงตารางเพื่อให้ได้จำนวนแถวโดยประมาณ ในความเป็นจริงค่าทั้งหมดนั้นแตกต่างกัน แต่ทั้งหมดตรงกับภาคแสดง

การติดตามquery_optimizer_estimate_cardinalityเหตุการณ์ที่ขยายแสดงว่าภายใต้ 130 มีสอง<StatsCollection Name="CStCollFilter"เหตุการณ์ คนแรกประมาณ 100,000 อันที่สองโหลดฮิสโตแกรมและใช้ CSelCalcPointPredsFreqBased / DistinctCountCalculator เพื่อรับค่าประมาณ 1.04 ผลลัพธ์ที่สองนี้ไม่ได้ใช้งาน

พฤติกรรมที่คุณสังเกตเห็นไม่ได้นำไปใช้อย่างสม่ำเสมอใน 130 ฉันเพิ่มORDER BY TheStringหวังว่าสิ่งนี้จะชนะอย่างชัดเจนสำหรับตัวประมาณ 130 เนื่องจากการต่อสู้ 120 ครั้งด้วยการให้สิทธิ์หน่วยความจำสำหรับหนึ่งแถว แต่การเปลี่ยนแปลงเล็กน้อยนี้เพียงพอที่จะทำให้แถวที่ประมาณลง 1.03913 ใน 130 กรณีด้วย

การเพิ่มOPTION (QUERYRULEOFF SelectToFilter)การประมาณค่ากลับเข้าไปในการเรียงลำดับเป็น 100,000 แต่การให้สิทธิ์หน่วยความจำจะไม่เพิ่มขึ้นและการประมาณการที่ออกมาเรียงลำดับจะยังคงขึ้นอยู่กับค่าที่แตกต่างของตาราง

ป้อนคำอธิบายรูปภาพที่นี่

ในทำนองเดียวกันการปรับเกณฑ์ค่าใช้จ่ายสำหรับการขนานเพื่อให้แบบสอบถามได้รับแผนขนานก็เพียงพอใน 130 กรณีที่จะเปลี่ยนกลับไปเป็นประมาณการที่ต่ำกว่า การเพิ่มQUERYTRACEON 8757ยังทำให้การประมาณการลดลง ดูเหมือนว่าประมาณการ 10% จะถูกเก็บไว้สำหรับแผนการที่ไม่สำคัญเท่านั้น

ข้อเสนอของคุณเขียนใหม่ด้วย

WHERE TheString LIKE 'ZZ[_]%'

แสดงการประมาณการที่เหนือกว่าทั้งสองอย่าง ผลลัพธ์สำหรับสิ่งนี้คือ

  CSelCalcTrieBased

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

แสดงให้เห็นว่ามันเคยพยายาม ข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ในส่วนของสตริงสถิติสรุปเหนือที่นี่

มันไม่เหมือนกับแบบสอบถามต้นฉบับของคุณอย่างไรก็ตาม เนื่องจากอินสแตนซ์แรกของ_ตอนนี้สันนิษฐานว่าเป็นอักขระที่สามเสมอแทนที่จะถูกพบแบบไดนามิก

หากข้อสันนิษฐานนี้ถูก hardcoded ลงในแบบสอบถามต้นฉบับของคุณ

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

วิธีการประเมินเปลี่ยนไปCSelCalcHistogramComparison(INTERVAL)และแถวที่ประมาณนั้นถูกต้อง

มันสามารถแปลงมันให้อยู่ในช่วง

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

และใช้ฮิสโตแกรมเพื่อประมาณจำนวนแถวที่มีค่าในช่วงนั้น

สิ่งนี้ใช้กับการประเมินความสำคัญเชิงการเต้นเท่านั้น LIKEเป็นที่นิยมมากกว่าเพราะสามารถใช้ช่วงค้นหาที่รันไทม์ SUBSTRING(TheString, 1, 3)หรือLEFT(TheString, 3)ไม่สามารถ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.