Stima della cardinalità SARG, perché non eseguire la scansione completa?


11

Perché non è presente la scansione completa (su SQL 2008 R2 e 2012)?

Dati di test:

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

Quando si esegue la query:

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

Ricevi un avviso (come previsto, perché confrontando i dati nchar con la colonna varchar):

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

Ma poi vedo il piano di esecuzione e vedo che non sta usando la scansione completa come mi aspetterei, ma cerca invece l'indice.

inserisci qui la descrizione dell'immagine

Certo, questo è abbastanza buono, perché in questo caso particolare l'esecuzione è molto più veloce rispetto a una scansione completa.

Ma non riesco a capire come il server SQL abbia preso la decisione di fare questo piano.

Inoltre, se le regole di confronto del server saranno regole di confronto di Windows a livello di server e database di regole di confronto di SQL Server, causerebbe la scansione completa sulla stessa query.

Risposte:


8

Quando si confrontano valori di tipi di dati diversi, SQL Server seguire le regole Precedenza tipo di dati . Poiché nvarchar ha una precedenza maggiore rispetto a varchar, SQL Server deve convertire i dati della colonna in nvarchar prima di confrontare i valori. Ciò significa applicare una funzione sulla colonna e renderebbe la query non effettuabile.

SQL Server fa comunque del suo meglio per proteggerti dai tuoi errori, quindi utilizza una tecnica descritta da Paul White nel post di blog Dynamic Seeks e Hidden Implicit Conversions per fare una ricerca di un intervallo di valori e quindi fare il confronto finale, con il conversione del valore della colonna in nvarchar, in un predicato residuo per filtrare eventuali falsi positivi.

Come hai notato, ciò tuttavia non funziona quando le regole di confronto della colonna sono regole di confronto SQL. La ragione di ciò, credo, può essere trovata nell'articolo Confronto tra regole di confronto SQL e regole di confronto di Windows

Fondamentalmente, un confronto di Windows utilizza lo stesso algoritmo per varchar e nvarchar in cui un confronto SQL utilizza un algoritmo diverso per i dati varchar e lo stesso algoritmo di un confronto di Windows per i dati nvarchar.

Quindi passare da varchar a nvarchar in un confronto di Windows utilizzerà lo stesso algoritmo e SQL Server può produrre un intervallo di valori da, nel tuo caso, un valore letterale di nvarchar per ottenere righe dall'indice di colonna di confronto SQL varchar. Tuttavia, quando il confronto della colonna varchar è un confronto SQL che non è possibile a causa del diverso algoritmo utilizzato.


Aggiornare:

Una dimostrazione dei diversi ordinamenti per le colonne varchar usando windows e sql collation.

SQL Fiddle

Installazione schema di MS SQL Server 2014 :

create table T(C varchar(10));

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

Query 1 :

select C
from T
order by C collate SQL_Latin1_General_CP1_CI_AS;

Risultati :

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

Query 2 :

select C
from T
order by C collate Latin1_General_100_CI_AS;

Risultati :

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

0

È necessario ricordare che i nodi foglia di un indice non cluster sono costituiti da pagine di indice che contengono chiave di cluster o RID per individuare la riga di dati.

Nella clausola where dichiari VeryRandomText = N'111'Dato che esiste un indice non cluster su VeryRandomText (crea un indice creerà un indice non cluster a meno che tu non gli dica esplicitamente di creare un cluster) il modo più economico per trovare i dati è scansionare l'indice per trovare il rowid e quindi recuperare i dati per la riga.

Se si desidera creare un indice cluster

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

o una chiave primaria su VeryRandomText otterrai una scansione di quell'indice.

Vedi i libri online o qui: http://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap


Sì, sono consapevole di ciò che stai scrivendo. Come puoi vedere, esiste già un indice cluster su TestTableID. Ma il fatto è che se il server SQL non può visualizzare le statistiche sulla distribuzione dei dati delle colonne (come in questo caso, a causa della mancata corrispondenza del tipo di dati che dovrebbe richiedere la conversione di tutti i tipi di dati del valore di riga), in questo caso dovrebbe scegliere Scansione indice cluster, non ricerca indice .
Jānis,

E non è sempre più economico cercare / scansionare un indice non cluster- quando i valori non sono abbastanza distinti o non coprono un indice, può essere più economico fare una scansione dell'indice cluster.
Jānis,

@ Jānis non accedendo al tuo script crea indice non creerà un indice cluster che devi dire esplicitamente - lo stesso se leggi il piano di query, ricerca indice (non cluster)
Spörri

"Quando si crea un vincolo PRIMARY KEY, viene creato automaticamente un indice cluster univoco sulla colonna o sulle colonne se un indice cluster sulla tabella non esiste già e non si specifica un indice univoco non cluster." msdn.microsoft.com/en-us/library/ms186342.aspx
Jānis
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.