Perkiraan kardinalitas SARG, mengapa tidak dipindai penuh?

11

Mengapa tidak ada pemindaian lengkap (Pada SQL 2008 R2 dan 2012)?

Data uji:

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

Saat menjalankan kueri:

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

Dapatkan peringatan (seperti yang diharapkan, karena membandingkan data nchar dengan kolom varchar):

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

Tapi kemudian saya melihat rencana eksekusi, dan saya bisa melihat, bahwa itu tidak menggunakan full-scan seperti yang saya harapkan, tetapi indeks yang mencari.

masukkan deskripsi gambar di sini

Tentu saja, ini agak baik, karena dalam kasus khusus ini eksekusi lebih cepat daripada jika akan ada pemindaian penuh.

Tetapi saya tidak bisa mengerti bagaimana SQL server mengambil keputusan untuk membuat rencana ini.

Juga - jika susunan server akan menjadi susunan Windows pada tingkat server dan tingkat basis data susunan SQL Server, maka itu akan menyebabkan pemindaian penuh pada permintaan yang sama.

Jānis
sumber

Jawaban:

8

Ketika membandingkan nilai dari tipe data yang berbeda, SQL Server mengikuti aturan Presedensi Tipe Data . Karena nvarchar memiliki prioritas lebih tinggi daripada varchar SQL Server harus mengkonversi data kolom ke nvarchar sebelum membandingkan nilai. Itu berarti menerapkan fungsi pada kolom dan itu akan membuat kueri tidak dapat ditagih.

Namun SQL Server melakukan yang terbaik untuk melindungi Anda dari kesalahan Anda sehingga menggunakan teknik yang dijelaskan oleh Paul White dalam posting blog Dynamic Seeks dan Hidden Implicit Conversions untuk mencari kisaran nilai dan kemudian melakukan perbandingan akhir, dengan konversi nilai kolom ke nvarchar, dalam predikat residual untuk menyaring semua positif palsu.

Seperti yang telah Anda catat, ini tidak bekerja ketika susunan kolom adalah susunan SQL. Alasan untuk itu, saya percaya, dapat ditemukan di artikel Membandingkan SQL collations dengan Windows collations

Pada dasarnya, Windows collation menggunakan algoritma yang sama untuk varchar dan nvarchar di mana collation SQL menggunakan algoritma yang berbeda untuk data varchar dan algoritma yang sama dengan collation Windows untuk data nvarchar.

Jadi beralih dari varchar ke nvarchar di bawah Windows collation akan menggunakan algoritma yang sama dan SQL Server dapat menghasilkan berbagai nilai dari, dalam kasus Anda, nvarchar literal untuk mendapatkan baris dari indeks kolom collation SQL varchar. Namun, ketika susunan kolom varchar adalah SQL Collation yang tidak mungkin karena algoritma yang digunakan berbeda.


Memperbarui:

Demonstrasi dari berbagai urutan pesanan untuk kolom varchar menggunakan windows dan sql collation.

SQL Fiddle

Setup Skema MS SQL Server 2014 :

create table T(C varchar(10));

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

Pertanyaan 1 :

select C
from T
order by C collate SQL_Latin1_General_CP1_CI_AS;

Hasil :

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

Pertanyaan 2 :

select C
from T
order by C collate Latin1_General_100_CI_AS;

Hasil :

|   C |
|-----|
|  aa |
| a-b |
|  ac |
Mikael Eriksson
sumber
0

Anda harus ingat bahwa simpul daun dari Indeks Nonclustered terdiri dari halaman Indeks yang berisi Kunci Clustering atau RID untuk menemukan Baris Data.

Di mana Anda klausa Anda menyatakan VeryRandomText = N'111'Karena ada indeks Non clustered pada VeryRandomText (buat indeks akan membuat indeks non clustered kecuali Anda secara eksplisit memberitahu itu untuk membuat clustered) cara termurah untuk menemukan data adalah dengan memindai indeks untuk menemukan rowid dan lalu ambil data untuk baris tersebut.

Jika Anda ingin membuat indeks berkerumun

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

atau kunci utama pada VeryRandomText Anda akan mendapatkan scan indeks itu.

Lihat buku online atau di sini: http://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap

Spörri
sumber
Ya, saya tahu apa yang Anda tulis. Seperti yang Anda lihat, sudah ada indeks berkerumun di TestTableID. Tetapi masalahnya adalah - jika SQL server tidak dapat melihat statistik distribusi data kolom (seperti dalam kasus ini, karena ketidakcocokan tipe data yang harus memerlukan semua konversi tipe data nilai baris), ia harus memilih pemindaian indeks Clustered dalam kasus ini, bukan pencarian indeks .
Jānis
Dan itu tidak selalu termurah untuk mencari / memindai indeks non-clustered - ketika nilai tidak cukup atau indeks tidak mencakup, mungkin lebih murah untuk melakukan scan indeks clustered sebagai gantinya.
Jānis
@ Jānis yang tidak menerima indeks pembuatan skrip Anda tidak akan membuat indeks berkerumun yang harus Anda katakan secara eksplisit - sama jika Anda membaca rencana kueri, pencarian indeks (nonclustered)
Spörri
"Ketika Anda membuat batasan PRIMARY KEY, indeks berkerumun unik pada kolom atau kolom secara otomatis dibuat jika indeks berkerumun pada tabel belum ada dan Anda tidak menentukan indeks nonclustered unik." msdn.microsoft.com/en-us/library/ms186342.aspx
Jānis