Los escaneos constantes producen cada uno una fila en memoria sin columnas. El escalar de cómputo superior genera una sola fila con 3 columnas
Expr1005 Expr1006 Expr1004
----------- ----------- -----------
NULL NULL 60
El escalar de proceso inferior genera una sola fila con 3 columnas
Expr1008 Expr1009 Expr1007
----------- ----------- -----------
NULL 1048576 10
El operador de concatenación une estas 2 filas y genera las 3 columnas, pero ahora se renombra
Expr1010 Expr1011 Expr1012
----------- ----------- -----------
NULL NULL 60
NULL 1048576 10
La Expr1012
columna es un conjunto de indicadores utilizados internamente para definir ciertas propiedades de búsqueda para Storage Engine .
El siguiente cálculo escalar a lo largo de las salidas 2 filas
Expr1010 Expr1011 Expr1012 Expr1013 Expr1014 Expr1015
----------- ----------- ----------- ----------- ----------- -----------
NULL NULL 60 True 4 16
NULL 1048576 10 False 0 0
Las últimas tres columnas se definen de la siguiente manera y solo se usan para fines de clasificación antes de presentarlas al Operador del intervalo de fusión
[Expr1013] = Scalar Operator(((4)&[Expr1012]) = (4) AND NULL = [Expr1010]),
[Expr1014] = Scalar Operator((4)&[Expr1012]),
[Expr1015] = Scalar Operator((16)&[Expr1012])
Expr1014
y Expr1015
solo pruebe si ciertos bits están activados en la bandera.
Expr1013
parece devolver una columna booleana verdadera si tanto el bit for 4
está activado como si Expr1010
está NULL
activado
Al probar otros operadores de comparación en la consulta obtengo estos resultados
+----------+----------+----------+-------------+----+----+---+---+---+---+
| Operator | Expr1010 | Expr1011 | Flags (Dec) | Flags (Bin) |
| | | | | 32 | 16 | 8 | 4 | 2 | 1 |
+----------+----------+----------+-------------+----+----+---+---+---+---+
| > | 1048576 | NULL | 6 | 0 | 0 | 0 | 1 | 1 | 0 |
| >= | 1048576 | NULL | 22 | 0 | 1 | 0 | 1 | 1 | 0 |
| <= | NULL | 1048576 | 42 | 1 | 0 | 1 | 0 | 1 | 0 |
| < | NULL | 1048576 | 10 | 0 | 0 | 1 | 0 | 1 | 0 |
| = | 1048576 | 1048576 | 62 | 1 | 1 | 1 | 1 | 1 | 0 |
| IS NULL | NULL | NULL | 60 | 1 | 1 | 1 | 1 | 0 | 0 |
+----------+----------+----------+-------------+----+----+---+---+---+---+
De lo cual deduzco que el Bit 4 significa "Tiene inicio de rango" (en lugar de no estar limitado) y el Bit 16 significa que el inicio del rango es inclusivo.
Este conjunto de resultados de 6 columnas se emite desde el SORT
operador ordenado por
Expr1013 DESC, Expr1014 ASC, Expr1010 ASC, Expr1015 DESC
. Asumir que True
está representado por 1
y False
por 0
el conjunto de resultados representado anteriormente ya está en ese orden.
Según mis supuestos anteriores, el efecto neto de este tipo es presentar los rangos al intervalo de fusión en el siguiente orden
ORDER BY
HasStartOfRangeAndItIsNullFirst,
HasUnboundedStartOfRangeFirst,
StartOfRange,
StartOfRangeIsInclusiveFirst
El operador de intervalo de fusión genera 2 filas
Expr1010 Expr1011 Expr1012
----------- ----------- -----------
NULL NULL 60
NULL 1048576 10
Para cada fila emitida se realiza una búsqueda de rango
Seek Keys[1]: Start:[dbo].[t].c2 > Scalar Operator([Expr1010]),
End: [dbo].[t].c2 < Scalar Operator([Expr1011])
Entonces parecería que se realizan dos búsquedas. Uno aparentemente > NULL AND < NULL
y uno > NULL AND < 1048576
. Sin embargo, las banderas que se pasan parecen modificar esto IS NULL
y < 1048576
respectivamente. ¡Ojalá @sqlkiwi pueda aclarar esto y corregir cualquier inexactitud!
Si cambia la consulta ligeramente a
select *
from t
where
c2 > 1048576
or c2 = 0
;
Entonces el plan se ve mucho más simple con una búsqueda de índice con múltiples predicados de búsqueda.
El plan muestra Seek Keys
Start: c2 >= 0, End: c2 <= 0,
Start: c2 > 1048576
SQLKiwi da la explicación de por qué este plan más simple no puede usarse para el caso en el OP en los comentarios a la publicación de blog vinculada anteriormente .
Una búsqueda de índice con múltiples predicados no puede mezclar diferentes tipos de predicados de comparación (es decir, Is
y Eq
en el caso del OP). Esta es solo una limitación actual del producto (y es presumiblemente la razón por la cual c2 = 0
se implementa la prueba de igualdad en la última consulta >=
y <=
no solo la búsqueda de igualdad directa que obtiene para la consulta c2 = 0 OR c2 = 1048576
.
62
es para una comparación de igualdad. Supongo que60
debe significar que, en lugar de lo> AND <
que se muestra en el plan, de hecho se obtiene, a>= AND <=
menos que sea una marca explícitaIS NULL
, tal vez (?) O tal vez el bit2
indique algo más no relacionado y60
siga siendo igual que cuando lo hagoset ansi_nulls off
y lo cambio ac2 = null
él, todavía permanece en60
Los escaneos constantes son una forma para que SQL Server cree un depósito en el que colocará algo más adelante en el plan de ejecución. He publicado una explicación más completa de esto aquí . Para comprender para qué sirve la exploración constante, debe profundizar en el plan. En este caso, son los operadores Compute Scalar los que se utilizan para completar el espacio creado por el escaneo constante.
Los operadores de Compute Scalar se están cargando con NULL y el valor 1045876, por lo que claramente se usarán con Loop Join en un esfuerzo por filtrar los datos.
La parte realmente genial es que este plan es trivial. Significa que pasó por un proceso de optimización mínimo. Todas las operaciones conducen al intervalo de fusión. Esto se utiliza para crear un conjunto mínimo de operadores de comparación para una búsqueda de índice ( detalles sobre eso aquí ).
La idea es deshacerse de los valores superpuestos para que luego pueda extraer los datos con pases mínimos. Aunque todavía está usando una operación de bucle, notará que el bucle se ejecuta exactamente una vez, lo que significa que es efectivamente un escaneo.
ADENDA: Esa última oración está apagada. Hubo dos búsquedas. Leí mal el plan. El resto de los conceptos son los mismos y el objetivo, pases mínimos, es el mismo.
fuente