การประมาณค่า SARG cardinality ทำไมไม่สแกนแบบเต็ม?


11

เหตุใดจึงไม่มีการสแกนเต็มรูปแบบ (ใน SQL 2008 R2 และ 2012)

ข้อมูลการทดสอบ:

DROP TABLE dbo.TestTable
GO  
CREATE TABLE dbo.TestTable
(
   TestTableID INT IDENTITY PRIMARY KEY,
   VeryRandomText VarChar(50),
   VeryRandomText2 VarChar(50)
)
Go
Set NoCount ON
Declare @i int
Set @i = 0
While @i < 10000
Begin
   Insert Into dbo.TestTable(VeryRandomText, VeryRandomText2)
      Values(Cast(Rand()*10000000 as VarChar(50)), Cast(Rand()*10000000 as VarChar(50)));
   Set @i = @i + 1;
End
Go
CREATE Index IX_VeryRandomText On dbo.TestTable
(
    VeryRandomText
)
Go

เมื่อเรียกใช้คิวรี:

Select * From dbo.TestTable Where VeryRandomText = N'111' -- bad

รับคำเตือน (ตามที่คาดไว้เนื่องจากการเปรียบเทียบข้อมูล nchar กับคอลัมน์ varchar):

<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(nvarchar(50),[DemoDatabase].[dbo].[TestTable].[VeryRandomText],0)" />

แต่จากนั้นฉันเห็นแผนการดำเนินการและฉันสามารถเห็นได้ว่ามันไม่ได้ใช้การสแกนแบบเต็มตามที่ฉันคาดหวัง แต่การค้นหาดัชนีแทน

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

แน่นอนว่าเป็นสิ่งที่ดีเพราะในกรณีนี้การดำเนินการโดยเฉพาะอย่างยิ่งเป็นวิธีที่เร็วกว่าถ้าจะมีการสแกนแบบเต็ม

แต่ฉันไม่สามารถเข้าใจว่าเซิร์ฟเวอร์ SQL ตัดสินใจเลือกแผนนี้อย่างไร

นอกจากนี้หากการเปรียบเทียบเซิร์ฟเวอร์จะเป็น Windows collations ในระดับเซิร์ฟเวอร์และระดับฐานข้อมูลการเปรียบเทียบ SQL Server ก็จะทำให้เกิดการสแกนแบบเต็มในแบบสอบถามเดียวกัน

คำตอบ:


8

เมื่อเปรียบเทียบค่าของประเภทข้อมูลที่แตกต่างกันของ SQL Server ให้ปฏิบัติตามกฎการลำดับความสำคัญของประเภทข้อมูล เนื่องจาก nvarchar มีลำดับความสำคัญสูงกว่า varchar SQL Server จึงต้องแปลงข้อมูลคอลัมน์เป็น nvarchar ก่อนเปรียบเทียบค่า นั่นหมายถึงการใช้ฟังก์ชั่นในคอลัมน์และนั่นจะทำให้แบบสอบถามไม่สามารถระบุเป้าหมายได้

อย่างไรก็ตาม SQL Server จะทำดีที่สุดเพื่อปกป้องคุณจากข้อผิดพลาดดังนั้นจึงใช้เทคนิคที่อธิบายโดย Paul White ในบล็อกโพสต์Dynamic Seeks และ Hidden Implicit Conversionเพื่อค้นหาช่วงของค่าจากนั้นทำการเปรียบเทียบขั้นสุดท้ายโดยใช้ การแปลงค่าคอลัมน์เป็น nvarchar ในเพรดิเคตที่เหลือเพื่อกรองผลบวกปลอมใด ๆ

ตามที่คุณได้จดบันทึกสิ่งนี้จะไม่ทำงานเมื่อการเรียงคอลัมน์เป็นการเปรียบเทียบ SQL ฉันเชื่อว่าเหตุผลนั้นสามารถพบได้ในบทความเปรียบเทียบการเปรียบเทียบ SQL กับการเปรียบเทียบ Windows

โดยทั่วไปการเปรียบเทียบ Windows จะใช้อัลกอริทึมเดียวกันสำหรับ varchar และ nvarchar ซึ่งการเปรียบเทียบ SQL ใช้อัลกอริทึมที่แตกต่างกันสำหรับข้อมูล varchar และอัลกอริทึมเดียวกันกับการเปรียบเทียบ Windows สำหรับข้อมูล nvarchar

ดังนั้นการเปลี่ยนจาก varchar เป็น nvarchar ภายใต้การจัดเรียงของ Windows จะใช้อัลกอริทึมเดียวกันและ SQL Server สามารถสร้างช่วงของค่าจากในกรณีของคุณตัวอักษร nvarchar ตามตัวอักษรเพื่อรับแถวจากดัชนีคอลัมน์การเรียง varchar SQL อย่างไรก็ตามเมื่อการเรียงคอลัมน์ varchar เป็นการเปรียบเทียบ SQL ที่เป็นไปไม่ได้เนื่องจากอัลกอริทึมที่แตกต่างกันที่ใช้


ปรับปรุง:

การสาธิตการเรียงลำดับที่แตกต่างกันสำหรับคอลัมน์ varchar โดยใช้ windows และ sql collation

ซอ Fiddle

MS SQL Server 2014 ติดตั้ง Schema :

create table T(C varchar(10));

insert into T values('a-b'),('aa'),('ac');

แบบสอบถาม 1 :

select C
from T
order by C collate SQL_Latin1_General_CP1_CI_AS;

ผลลัพธ์ :

|   C |
|-----|
| a-b |
|  aa |
|  ac |

แบบสอบถาม 2 :

select C
from T
order by C collate Latin1_General_100_CI_AS;

ผลลัพธ์ :

|   C |
|-----|
|  aa |
| a-b |
|  ac |

0

คุณต้องจำไว้ว่าโหนดใบไม้ของ Nonclustered Index ประกอบด้วยหน้าดัชนีซึ่งมี Clustering Key หรือ RID เพื่อค้นหาแถวข้อมูล

ในส่วนคำสั่งของคุณที่คุณระบุVeryRandomText = N'111'เนื่องจากมีดัชนีที่ไม่ใช่คลัสเตอร์บน VeryRandomText (สร้างดัชนีจะสร้างดัชนีที่ไม่ได้ทำคลัสเตอร์เว้นแต่คุณจะบอกให้สร้างกลุ่มอย่างชัดเจน) วิธีที่ถูกที่สุดในการค้นหาข้อมูลคือการสแกนดัชนีเพื่อค้นหาแถวและ จากนั้นดึงข้อมูลสำหรับแถว

หากคุณจะสร้างดัชนีคลัสเตอร์

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

หรือคีย์หลักใน VeryRandomText คุณจะได้รับการสแกนดัชนีนั้น

ดูหนังสือออนไลน์หรือที่นี่: http://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap


ใช่ฉันรู้ว่าคุณเขียนอะไร อย่างที่คุณเห็นมีดัชนีคลัสเตอร์อยู่แล้วใน TestTableID แต่สิ่งนี้คือ - ถ้าเซิร์ฟเวอร์ SQL ไม่สามารถดูสถิติของการกระจายข้อมูลคอลัมน์ (เช่นในกรณีนี้เนื่องจากชนิดข้อมูลไม่ตรงกันที่ควรจะต้องมีการแปลงค่าของข้อมูลในแถวทั้งหมด) ก็ควรเลือกการสแกนดัชนีแบบคลัสเตอร์ในกรณีนี้ .
Jānis

และไม่ถูกที่สุดเสมอในการค้นหา / สแกนดัชนีที่ไม่ใช่คลัสเตอร์ - เมื่อค่าไม่ชัดเจนเพียงพอหรือไม่ครอบคลุมดัชนีอาจมีราคาถูกกว่าในการสแกนดัชนีแบบกลุ่มแทน
Jānis

@ Jānไม่ได้ยกย่องให้สคริปต์ของคุณสร้างดัชนีจะไม่สร้างดัชนีกลุ่มที่คุณต้องพูดอย่างชัดเจน - เช่นเดียวกันถ้าคุณอ่านแผนแบบสอบถามค้นหาดัชนี (nonclustered)
Spörri

"เมื่อคุณสร้างข้อ จำกัด KEY KEY ดัชนีคลัสเตอร์ที่ไม่ซ้ำกันในคอลัมน์หรือคอลัมน์จะถูกสร้างขึ้นโดยอัตโนมัติหากดัชนีคลัสเตอร์บนตารางนั้นไม่มีอยู่แล้วและคุณไม่ได้ระบุดัชนีที่ไม่ซ้ำแบบคลัสเตอร์" msdn.microsoft.com/en-us/library/ms186342.aspx
Jānis
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.