¿Por qué la variable de tabla obliga a una exploración de índice mientras que la tabla temporal utiliza la búsqueda y la búsqueda de marcadores?

18

Estoy tratando de entender por qué el uso de una variable de tabla impide que el optimizador use una búsqueda de índice y luego la búsqueda de marcadores frente a una exploración de índice.

Poblar la mesa:

CREATE TABLE dbo.Test 
(
    RowKey INT NOT NULL PRIMARY KEY, 
    SecondColumn CHAR(1) NOT NULL DEFAULT 'x',
    ForeignKey INT NOT NULL 
) 

INSERT dbo.Test 
(
    RowKey, 
    ForeignKey
) 
SELECT TOP 1000000 
    ROW_NUMBER() OVER (ORDER BY (SELECT 0)),
    ABS(CHECKSUM(NEWID()) % 10)     
FROM sys.all_objects s1
CROSS JOIN sys.all_objects s2 

CREATE INDEX ix_Test_1 ON dbo.Test (ForeignKey) 

Rellene una variable de tabla con un único registro e intente buscar la clave primaria y la segunda columna buscando en la columna de clave externa:

DECLARE @Keys TABLE (RowKey INT NOT NULL) 

INSERT @Keys (RowKey) VALUES (10)

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey

A continuación se muestra el plan de ejecución:

ingrese la descripción de la imagen aquí

Ahora la misma consulta usando una tabla temporal en su lugar:

CREATE TABLE #Keys (RowKey INT NOT NULL) 

INSERT #Keys (RowKey) VALUES (10) 

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    #Keys k
ON
    t.ForeignKey = k.RowKey

Este plan de consulta utiliza una búsqueda y búsqueda de marcadores:

ingrese la descripción de la imagen aquí

¿Por qué el optimizador está dispuesto a hacer la búsqueda de marcadores con la tabla temporal, pero no con la variable de la tabla?

La variable de tabla se usa en este ejemplo para representar datos que provienen de un tipo de tabla definida por el usuario en un procedimiento almacenado.

Me doy cuenta de que la búsqueda del índice podría no ser apropiada si el valor de la clave externa se produjo cientos de miles de veces. En ese caso, un escaneo probablemente sería una mejor opción. Para el escenario que creé, no había una fila con un valor de 10. Todavía creo que el comportamiento es interesante y me gustaría saber si hay una razón para ello.

Violín de SQL

Agregar OPTION (RECOMPILE)no cambió el comportamiento. El UDDT tiene una clave primaria.

@@VERSION es SQL Server 2008 R2 (SP2) - 10.50.4042.0 (X64) (compilación 7601: Service Pack 1) (hipervisor)

8kb
fuente

Respuestas:

15

La razón del comportamiento es que SQL Server no puede determinar cuántas filas coincidirán con ForeignKey, ya que no hay un índice con RowKey como columna principal (puede deducir esto de las estadísticas en la tabla #temp, pero esas no existen para variables de tabla / UDTT), por lo que hace una estimación de 100,000 filas, que se maneja mejor con un escaneo que una búsqueda + búsqueda. Cuando SQL Server se da cuenta de que solo hay una fila, es demasiado tarde.

Es posible que pueda construir su UDTT de manera diferente; en versiones más modernas de SQL Server puede crear índices secundarios en variables de tabla, pero esta sintaxis no está disponible en 2008 R2.

Por cierto, puede obtener el comportamiento de búsqueda (al menos en mis pruebas limitadas) si intenta evitar el mapa de bits / sonda insinuando una unión de bucles anidados:

DECLARE @Keys TABLE (RowKey INT PRIMARY KEY); -- can't hurt

INSERT @Keys (RowKey) VALUES (10);

SELECT 
     t.RowKey
    ,t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey
    OPTION (LOOP JOIN);

Me enteré de este truco de Paul White hace varios años. Por supuesto, debe tener cuidado al poner cualquier tipo de sugerencias de unión en el código de producción; esto puede fallar si las personas realizan cambios en los objetos subyacentes y ese tipo específico de unión ya no es posible o ya no es el más óptimo.

Para consultas más complejas, y cuando pasa a SQL Server 2012 o superior, es posible que la marca de seguimiento 2453 pueda ayudar. Sin embargo, esa bandera no ayudó con esta simple unión. Y se aplicarían los mismos descargos de responsabilidad: esto es solo una cosa alternativa que generalmente no debería hacer sin una tonelada de documentación y procedimientos de prueba de regresión rigurosos.

Además, el Service Pack 1 está fuera de servicio por mucho tiempo, debería obtener el Service Pack 3 + MS15-058 .

Aaron Bertrand
fuente
3

Las variables de tabla y las tablas temporales se manejan de manera diferente de varias maneras. Aquí hay una gran respuesta con muchos detalles sobre dónde son diferentes.

Específicamente en su caso, supongo que el culpable es el hecho de que las tablas temporales pueden generar estadísticas adicionales y planes paralelos, mientras que las variables de tabla tienen estadísticas más limitadas (sin estadísticas de nivel de columna) y sin planes paralelos.

Es mejor que descargue la variable de tabla en una tabla temporal mientras dure el procedimiento almacenado.

Kenneth Fisher
fuente