Estimación de la cardinalidad SARG, ¿por qué no realizar un escaneo completo?

11

¿Por qué no hay exploración completa (en SQL 2008 R2 y 2012)?

Datos de prueba:

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

Cuando ejecuta la consulta:

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

Obtenga una advertencia (como se esperaba, porque compara los datos de nchar con la columna varchar):

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

Pero luego veo el plan de ejecución, y puedo ver, que no está usando el escaneo completo como esperaría, sino la búsqueda de índice.

ingrese la descripción de la imagen aquí

Por supuesto, esto es bastante bueno, porque en este caso particular la ejecución es mucho más rápida que si hubiera un escaneo completo.

Pero no puedo entender cómo el servidor SQL tomó la decisión de hacer este plan.

Además, si la intercalación del servidor sería intercalación de Windows en el nivel del servidor y el nivel de la base de datos de intercalación de SQL Server, provocaría una exploración completa en la misma consulta.

Jānis
fuente

Respuestas:

8

Al comparar valores de diferentes tipos de datos, SQL Server sigue las reglas de Precedencia de tipo de datos . Dado que nvarchar tiene mayor prioridad que varchar, SQL Server tiene que convertir los datos de la columna a nvarchar antes de comparar valores. Eso significa aplicar una función en la columna y eso haría que la consulta no sea sargable.

Sin embargo, SQL Server hace lo mejor para protegerlo de sus errores, por lo que utiliza una técnica descrita por Paul White en la publicación de blog Dynamic Seeks and Hidden Implicit Conversions para buscar un rango de valores y luego hacer la comparación final, con el conversión del valor de la columna a nvarchar, en un predicado residual para filtrar cualquier falso positivo.

Como ha notado, esto no funciona cuando la clasificación de la columna es una clasificación SQL. La razón de eso, creo, se puede encontrar en el artículo Comparación de intercalaciones SQL con intercalaciones de Windows

Básicamente, una intercalación de Windows usa el mismo algoritmo para varchar y nvarchar, donde una intercalación SQL usa un algoritmo diferente para los datos de varchar y el mismo algoritmo que una intercalación de Windows para los datos de nvarchar.

Por lo tanto, pasar de varchar a nvarchar en una clasificación de Windows usará el mismo algoritmo y SQL Server puede producir un rango de valores, en su caso, un literal nvarchar para obtener filas del índice de la columna de clasificación de varchar SQL. Sin embargo, cuando la clasificación de la columna varchar es una Clasificación SQL que no es posible debido al diferente algoritmo utilizado.


Actualizar:

Una demostración de los diferentes órdenes de clasificación para columnas varchar utilizando ventanas y colación sql.

Violín de SQL

Configuración del esquema de MS SQL Server 2014 :

create table T(C varchar(10));

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

Consulta 1 :

select C
from T
order by C collate SQL_Latin1_General_CP1_CI_AS;

Resultados :

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

Consulta 2 :

select C
from T
order by C collate Latin1_General_100_CI_AS;

Resultados :

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

Debe recordar que los nodos hoja de un índice no agrupado consta de páginas de índice que contienen clave de agrupación o RID para ubicar la fila de datos.

En su cláusula where, usted indica VeryRandomText = N'111'Dado que hay un índice no agrupado en VeryRandomText (crear índice creará un índice no agrupado a menos que le indique explícitamente que cree un agrupamiento) la forma más barata de encontrar los datos es escanear el índice para encontrar el rowid y luego busque los datos para la fila.

Si creara un índice agrupado

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

o una clave principal en VeryRandomText obtendría un escaneo de ese índice.

Vea libros en línea o aquí: http://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap

Spörri
fuente
Sí, estoy al tanto de lo que escribes. Como puede ver, ya hay un índice agrupado en TestTableID. Pero la cuestión es que si el servidor SQL no puede ver las estadísticas de la distribución de datos de la columna (como en este caso, debido a la falta de coincidencia del tipo de datos que debería requerir la conversión de todo tipo de datos de valor de fila), debería elegir la exploración de índice agrupado en este caso, no la búsqueda de índice .
Jānis
Y no siempre es más barato buscar / escanear índice no agrupado: cuando los valores no son lo suficientemente distintos o no cubren el índice, puede ser más barato hacer un escaneo de índice agrupado.
Jānis
@ Jānis no está de acuerdo con su script para crear el índice no creará un índice agrupado que tenga que decir explícitamente, lo mismo si lee el plan de consulta, búsqueda de índice (no agrupado)
Spörri
"Cuando crea una restricción PRIMARY KEY, un índice agrupado único en la columna o columnas se crea automáticamente si un índice agrupado en la tabla ya no existe y no especifica un índice único no agrupado". msdn.microsoft.com/en-us/library/ms186342.aspx
Jānis