นี่คือการตัดสินใจของเครื่องมือเพิ่มประสิทธิภาพตามต้นทุน
ค่าใช้จ่ายโดยประมาณที่ใช้ในตัวเลือกนี้ไม่ถูกต้องเนื่องจากจะถือว่ามีความเป็นอิสระทางสถิติระหว่างค่าในคอลัมน์ต่างๆ
มันคล้ายกับปัญหาที่อธิบายไว้ในRow Goals Gone Rogueซึ่งจำนวนคู่และคี่มีความสัมพันธ์เชิงลบ
มันง่ายที่จะทำซ้ำ
CREATE TABLE dbo.animal(
id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
colour varchar(50) NOT NULL,
species varchar(50) NOT NULL,
Filler char(10) NULL
);
/*Insert 20 million rows with 1% black and 1% swan but no black swans*/
WITH T
AS (SELECT TOP 20000000 ROW_NUMBER() OVER (ORDER BY @@SPID) AS RN
FROM master..spt_values v1,
master..spt_values v2,
master..spt_values v3)
INSERT INTO dbo.animal
(colour,
species)
SELECT CASE
WHEN RN % 100 = 1 THEN 'black'
ELSE CAST(RN % 100 AS VARCHAR(3))
END,
CASE
WHEN RN % 100 = 2 THEN 'swan'
ELSE CAST(RN % 100 AS VARCHAR(3))
END
FROM T
/*Create some indexes*/
CREATE NONCLUSTERED INDEX ix_species ON dbo.animal(species);
CREATE NONCLUSTERED INDEX ix_colour ON dbo.animal(colour);
ตอนนี้ลอง
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black'
AND species LIKE 'swan'
นี้จะช่วยให้แผนด้านล่างซึ่งเป็น costed 0.0563167
ที่
แผนสามารถทำการรวมการผสานระหว่างผลลัพธ์ของดัชนีสองรายการในid
คอลัมน์ ( รายละเอียดเพิ่มเติมของการรวมการรวมอัลกอริทึมที่นี่ )
ผสานการเข้าร่วมต้องใช้ทั้งสองอินพุทโดยคีย์การเข้าร่วม
ดัชนีที่ไม่เป็นคลัสเตอร์ถูกจัดเรียงโดย(species, id)
และ(colour, id)
ตามลำดับ (ดัชนีที่ไม่ใช่คลัสเตอร์ที่ไม่ซ้ำกันจะมีตัวระบุแถวที่เพิ่มเข้าไปในส่วนท้ายของคีย์โดยนัยถ้าไม่ได้เพิ่มอย่างชัดเจน) แบบสอบถามโดยไม่สัญลักษณ์ใด ๆ ที่มีประสิทธิภาพเท่าเทียมกันขอเข้าและspecies = 'swan'
colour ='black'
เนื่องจากการค้นหาแต่ละครั้งจะดึงค่าที่แน่นอนจากคอลัมน์นำเท่านั้นแถวที่ตรงกันจะถูกเรียงลำดับตามid
ดังนั้นแผนนี้จึงเป็นไปได้
ผู้ประกอบการแผนแบบสอบถามรันจากซ้ายไปขวา ด้วยโอเปอเรเตอร์ด้านซ้ายที่ร้องขอแถวจากลูกของมันซึ่งจะขอแถวจากลูก ๆของพวกเขา (และต่อไปเรื่อย ๆ จนกว่าจะถึงโหนดใบไม้) ตัวTOP
วนซ้ำจะหยุดร้องขอแถวเพิ่มเติมจากลูกของมันเมื่อได้รับ 10 แล้ว
SQL Server มีสถิติของดัชนีที่บอกว่า 1% ของแถวตรงกับแต่ละภาคแสดง มันถือว่าสถิติเหล่านี้เป็นอิสระ (เช่นไม่มีความสัมพันธ์เชิงบวกหรือเชิงลบ) ดังนั้นโดยเฉลี่ยเมื่อมีการประมวลผล 1,000 แถวที่ตรงกับภาคแรกจะพบ 10 การจับคู่ที่สองและสามารถออก (แผนข้างต้นแสดงให้เห็นถึง 987 มากกว่า 1,000 แต่ใกล้พอ)
ในความเป็นจริงเนื่องจากภาคแสดงมีความสัมพันธ์เชิงลบกับแผนจริงแสดงให้เห็นว่าแถวที่ตรงกันทั้งหมด 200,000 แถวจำเป็นต้องถูกประมวลผลจากดัชนีแต่ละตัว แต่สิ่งนี้ได้รับการบรรเทาในระดับหนึ่งเพราะแถวเข้าร่วมเป็นศูนย์
เปรียบเทียบกับ
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
ซึ่งให้แผนด้านล่างซึ่งคิดต้นทุนอยู่ที่ 0.567943
การเพิ่ม wildcard ต่อท้ายทำให้เกิดการสแกนดัชนี ค่าใช้จ่ายของแผนยังค่อนข้างต่ำแม้ว่าจะสแกนบนตาราง 20 ล้านแถว
การเพิ่มquerytraceon 9130
แสดงข้อมูลเพิ่มเติมบางอย่าง
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
OPTION (QUERYTRACEON 9130)
จะเห็นได้ว่า SQL Server คิดว่ามันจะต้องสแกนประมาณ 100,000 แถวเท่านั้นก่อนที่มันจะพบ 10 เพรดิเคตที่ตรงกันและTOP
สามารถหยุดการร้องขอแถวได้
อีกครั้งนี้เหมาะสมกับการสันนิษฐานอิสรภาพ 10 * 100 * 100 = 100,000
ในที่สุดให้ลองและบังคับแผนแยกดัชนี
SELECT TOP 10 *
FROM animal WITH (INDEX(ix_species), INDEX(ix_colour))
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
นี่เป็นแผนคู่ขนานสำหรับฉันด้วยค่าใช้จ่ายประมาณ 3.4625
ความแตกต่างหลักที่นี่คือเพรดิเคตcolour like 'black%'
สามารถจับคู่หลายสีที่แตกต่างกัน id
ซึ่งหมายความว่าแถวดัชนีตรงกันสำหรับกริยาที่ไม่ได้รับประกันว่าจะถูกจัดเรียงในคำสั่งของ
ตัวอย่างเช่นการค้นหาดัชนีlike 'black%'
อาจส่งคืนแถวต่อไปนี้
+------------+----+
| Colour | id |
+------------+----+
| black | 12 |
| black | 20 |
| black | 23 |
| black | 25 |
| blackberry | 1 |
| blackberry | 50 |
+------------+----+
ภายในแต่ละสีรหัสจะถูกเรียงลำดับ แต่รหัสในสีที่ต่างกันอาจไม่เป็นเช่นนั้น
เป็นผลให้ SQL Server ไม่สามารถทำการแยกการรวมดัชนีการผสาน (โดยไม่ต้องเพิ่มตัวดำเนินการเรียงลำดับการบล็อค) และจะทำการแฮชเข้าร่วมแทน Hash Join กำลังปิดกั้นอินพุตบิลด์ดังนั้นตอนนี้ต้นทุนจะสะท้อนความจริงที่ว่าแถวที่ตรงกันทั้งหมดจะต้องถูกประมวลผลจากบิลด์บิลด์แทนที่จะคิดว่ามันจะต้องสแกน 1,000 เท่าในแผนแรก
อินพุตโพรบไม่ใช่การปิดกั้นและยังคงประเมินอย่างไม่ถูกต้องว่าจะสามารถหยุดการโพรบหลังจากประมวลผลแถว 987 จากนั้น
(ข้อมูลเพิ่มเติมเกี่ยวกับการไม่ปิดกั้นและการปิดกั้นตัวทำเครื่องหมายที่นี่)
เนื่องจากค่าใช้จ่ายที่เพิ่มขึ้นของแถวที่ประมาณพิเศษและการแฮชเข้าร่วมการสแกนดัชนีแบบคลัสเตอร์บางส่วนนั้นดูถูกกว่า
ในทางปฏิบัติแล้วแน่นอนว่าการสแกนดัชนีแบบกลุ่มบางส่วนนั้นไม่ได้เป็นบางส่วนและจำเป็นต้องใช้เวลาในการตรวจสอบทั้ง 20 ล้านแถวแทนที่จะเป็น 100,000 เมื่อเปรียบเทียบกับแผน
การเพิ่มมูลค่าของTOP
(หรือลบออกทั้งหมด) ในที่สุดก็พบจุดเปลี่ยนที่จำนวนแถวที่ประเมินการสแกน CI จะต้องครอบคลุมทำให้แผนนั้นดูมีราคาแพงกว่าและเปลี่ยนเป็นแผนตัดกันดัชนี สำหรับผมจุดตัดระหว่างสองแผนคือVSTOP (89)
TOP (90)
สำหรับคุณอาจแตกต่างกันอย่างมากเนื่องจากขึ้นอยู่กับความกว้างของดัชนีคลัสเตอร์
การลบTOP
และบังคับให้สแกน CI
SELECT *
FROM animal WITH (INDEX = 1)
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
มีค่าใช้จ่ายที่88.0586
เครื่องของฉันสำหรับตารางตัวอย่างของฉัน
ถ้า SQL Server ทราบว่าสวนสัตว์ไม่มีหงส์ดำและต้องทำการสแกนแบบเต็มแทนที่จะอ่านเพียง 100,000 แถวแผนนี้จะไม่ถูกเลือก
ฉันได้ลองสถิติหลายคอลัมน์ในanimal(species,colour)
และanimal(colour,species)
และกรองสถิติในanimal (colour) where species = 'swan'
แต่ไม่มีความช่วยเหลือเหล่านี้ทำให้มั่นใจได้ว่าหงส์ดำไม่มีอยู่และการTOP 10
สแกนจะต้องดำเนินการมากกว่า 100,000 แถว
นี่คือสาเหตุที่ "สมมติฐานการรวม" ที่ SQL Server เป็นหลักถือว่าถ้าคุณกำลังค้นหาสิ่งที่มันอาจมีอยู่
ในปี 2008+ มีการตั้งค่าสถานะการสืบค้นกลับเป็นเอกสาร 4138ซึ่งจะปิดเป้าหมายของแถว ผลของสิ่งนี้คือแผนมีการคิดต้นทุนโดยไม่มีข้อสันนิษฐานว่าTOP
จะอนุญาตให้ผู้ประกอบการเด็กยุติก่อนกำหนดโดยไม่ต้องอ่านแถวที่ตรงกันทั้งหมด เมื่อใช้การตั้งค่าสถานะการติดตามนี้ฉันจะได้รับแผนการแยกดัชนีที่เหมาะสมที่สุดตามธรรมชาติ
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
OPTION (QUERYTRACEON 4138)
แผนนี้มีต้นทุนอย่างถูกต้องสำหรับการอ่านเต็ม 200,000 แถวในดัชนีทั้งสองค้นหา แต่ค่าใช้จ่ายการค้นหาคีย์มากกว่า (ประมาณ 2 พันเทียบกับค่าจริง 0 TOP 10
จะ จำกัด สิ่งนี้ให้สูงสุด 10 แต่ค่าสถานะการติดตามป้องกันไม่ให้ถูกนำมาพิจารณา) . แผนยังคงมีราคาถูกกว่าการสแกน CI เต็มรูปแบบดังนั้นจึงถูกเลือก
แน่นอนแผนนี้อาจไม่เหมาะสำหรับชุดค่าผสมที่เป็นเรื่องธรรมดา เช่นหงส์ขาว
ดัชนีคอมโพสิตในanimal (colour, species)
หรือanimal (species, colour)
จะช่วยให้แบบสอบถามมีประสิทธิภาพมากขึ้นสำหรับทั้งสองสถานการณ์
ที่จะทำให้การใช้งานมีประสิทธิภาพมากที่สุดของดัชนีคอมโพสิตยังจะต้องมีการเปลี่ยนแปลงไปLIKE 'swan'
= 'swan'
ตารางด้านล่างแสดงเพรดิเคตค้นหาและเพรดิเคตที่เหลือที่แสดงในแผนการดำเนินการสำหรับการเปลี่ยนลำดับทั้งสี่
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
| WHERE clause | Index | Seek Predicate | Residual Predicate |
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
| colour LIKE 'black%' AND species LIKE 'swan' | ix_colour_species | colour >= 'black' AND colour < 'blacL' | colour like 'black%' AND species like 'swan' |
| colour LIKE 'black%' AND species LIKE 'swan' | ix_species_colour | species >= 'swan' AND species <= 'swan' | colour like 'black%' AND species like 'swan' |
| colour LIKE 'black%' AND species = 'swan' | ix_colour_species | (colour,species) >= ('black', 'swan')) AND colour < 'blacL' | colour LIKE 'black%' AND species = 'swan' |
| colour LIKE 'black%' AND species = 'swan' | ix_species_colour | species = 'swan' AND (colour >= 'black' and colour < 'blacL') | colour like 'black%' |
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
TOP
ค่าในตัวแปรหมายความว่ามันจะถือว่ามากกว่าTOP 100
TOP 10
สิ่งนี้อาจช่วยได้หรือไม่ขึ้นอยู่กับว่าจุดเปลี่ยนระหว่างสองแผนคืออะไร