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
.