SARG-Kardinalitätsschätzung, warum nicht Full-Scan?

11

Warum gibt es keinen vollständigen Scan (unter SQL 2008 R2 und 2012)?

Testdaten:

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

Bei der Ausführung der Abfrage:

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

Warnung erhalten (wie erwartet, da nchar-Daten mit der varchar-Spalte verglichen werden):

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

Aber dann sehe ich einen Ausführungsplan, und ich kann sehen, dass er nicht den erwarteten Full-Scan verwendet, sondern stattdessen die Indexsuche.

Geben Sie hier die Bildbeschreibung ein

Das ist natürlich gut, denn in diesem speziellen Fall ist die Ausführung viel schneller als bei einem vollständigen Scan.

Aber ich kann nicht verstehen, wie SQL Server zu der Entscheidung kam, diesen Plan zu machen.

Wenn die Serverkollatierung Windows-Kollatierungen auf Serverebene und SQL Server-Kollatierungsdatenbankebene wäre, würde dies einen vollständigen Scan für dieselbe Abfrage verursachen.

Jānis
quelle

Antworten:

8

Wenn Werte verschiedenen Datentypen zu vergleichen folgen SQL Server der Datentyp Präzedenz - Regeln. Da nvarchar eine höhere Priorität als varchar hat, muss SQL Server die Spaltendaten vor dem Vergleichen der Werte in nvarchar konvertieren. Das bedeutet, dass eine Funktion auf die Spalte angewendet wird, wodurch die Abfrage nicht sarkierbar wird.

SQL Server ist jedoch am besten geeignet, um Sie vor Ihren Fehlern zu schützen. Daher verwendet es eine von Paul White im Blogbeitrag Dynamic Seeks and Hidden Implicit Conversions beschriebene Technik, um nach einem Wertebereich zu suchen und anschließend den endgültigen Vergleich mit dem durchzuführen Konvertierung des Spaltenwerts in nvarchar in einem Restprädikat, um alle falsch positiven Ergebnisse herauszufiltern.

Wie Sie bereits bemerkt haben, funktioniert dies jedoch nicht, wenn die Sortierung der Spalte eine SQL-Sortierung ist. Der Grund dafür ist meines Erachtens im Artikel Vergleichen von SQL-Kollatierungen mit Windows-Kollatierungen zu finden

Grundsätzlich verwendet eine Windows-Kollatierung denselben Algorithmus für varchar und nvarchar, während eine SQL-Kollatierung einen anderen Algorithmus für varchar-Daten und denselben Algorithmus wie eine Windows-Kollatierung für nvarchar-Daten verwendet.

Wenn Sie also unter einer Windows-Kollatierung von varchar zu nvarchar wechseln, wird derselbe Algorithmus verwendet, und SQL Server kann aus Ihrem nvarchar-Literal einen Wertebereich erstellen, um Zeilen aus dem varchar SQL-Kollatierungsspaltenindex abzurufen. Wenn es sich bei der Sortierung der varchar-Spalte jedoch um eine SQL-Sortierung handelt, ist dies aufgrund des unterschiedlichen verwendeten Algorithmus nicht möglich.


Aktualisieren:

Eine Demonstration der verschiedenen Sortierreihenfolgen für Varchar-Spalten mithilfe von Fenstern und SQL-Sortierung.

SQL Fiddle

MS SQL Server 2014 Schema-Setup :

create table T(C varchar(10));

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

Abfrage 1 :

select C
from T
order by C collate SQL_Latin1_General_CP1_CI_AS;

Ergebnisse :

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

Abfrage 2 :

select C
from T
order by C collate Latin1_General_100_CI_AS;

Ergebnisse :

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

Sie müssen sich daran erinnern, dass die Blattknoten eines nicht gruppierten Index aus Indexseiten bestehen, die Clustering Key oder RID zum Auffinden der Datenzeile enthalten.

In Ihrer where-Klausel geben Sie VeryRandomText = N'111'an, dass VeryRandomText einen Index ohne Cluster erstellt (Index erstellen erstellt einen Index ohne Cluster, es sei denn, Sie weisen ihn ausdrücklich an, einen Cluster zu erstellen). Der billigste Weg, die Daten zu finden, besteht darin, den Index zu scannen, um die Zeilen-ID und zu finden Rufen Sie dann die Daten für die Zeile ab.

Wenn Sie einen Clustered-Index erstellen würden

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

Mit einem Primärschlüssel in VeryRandomText erhalten Sie einen Scan dieses Index.

Siehe Bücher online oder hier: http://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap

Spörri
quelle
Ja, ich weiß, was du schreibst. Wie Sie sehen, gibt es bereits einen Clustered-Index für TestTableID. Die Sache ist jedoch: Wenn SQL Server keine Statistiken zur Verteilung von Spaltendaten sehen kann (wie in diesem Fall aufgrund einer Nichtübereinstimmung des Datentyps, für die alle Zeilenwertdatentypkonvertierungen erforderlich sein sollten), sollte in diesem Fall Clustered Index Scan und nicht Indexsuche ausgewählt werden .
Jānis
Und es ist nicht immer am billigsten, nicht geclusterten Index zu suchen / zu scannen. Wenn die Werte nicht eindeutig genug sind oder den Index nicht abdecken, ist es möglicherweise billiger, stattdessen einen Clustered-Index-Scan durchzuführen.
Jānis
@ Jānis nicht nach Ihrem Skript erstellen Index erstellt keinen Clustered-Index, den Sie explizit sagen müssen - das gleiche gilt, wenn Sie den Abfrageplan lesen, Indexsuche (nicht gruppiert)
Spörri
"Wenn Sie eine PRIMARY KEY-Einschränkung erstellen, wird automatisch ein eindeutiger Clustered-Index für die Spalte oder Spalten erstellt, wenn noch kein Clustered-Index für die Tabelle vorhanden ist und Sie keinen eindeutigen Nonclustered-Index angeben." msdn.microsoft.com/en-us/library/ms186342.aspx
Jānis