Buscar predicado que no usa todas las columnas disponibles


8

Tengo un extraño problema de compilación de consultas que es difícil de reproducir. Solo ocurre bajo una carga alta y no se puede repetir fácilmente.

  • Hay una tabla T con las columnas A, B, C, D.
  • Hay un índice agrupado no único en T (A, B, C, D).
  • Hay una consulta SELECT * FROM T WHERE A = @ P1 AND B = @ P2 AND (C = @ P3 OR C = @ P4) AND D = @ P5. La condición de búsqueda está en todas las columnas del índice agrupado, la columna 3 tiene un OR.

¡El problema es que el plan de consulta para esta consulta tiene Buscar predicado solo en A y B! El predicado en C y D es un predicado ordinario, por lo que esto significa que el árbol de búsqueda en las columnas C y D no se utiliza.

Los tipos de datos para todos los parámetros coinciden con los tipos de datos de columna.

¿Alguien podría dar alguna pista sobre por qué esto podría estar sucediendo? La versión SQL es 2008 R2 (SP1) - 10.50.2789.0 (X64)


¿Alguna vez obtiene un plan para una consulta parametrizada que realiza una búsqueda de igualdad en las 4 columnas? Si es así, ¿estás usando OPTION (RECOMPILE)?
Martin Smith

Respuestas:


8

Para una consulta parametrizada No puede hacer dos búsquedas en

WHERE A=@P1 AND B=@P2 AND C=@P3 AND D=@P5 

y

WHERE A=@P1 AND B=@P2 AND C=@P4 AND D=@P5 

Porque si @P3 = @P4eso traería incorrectamente filas duplicadas. Por lo tanto, primero necesitaría un operador que eliminara los duplicados.

A partir de una prueba rápida, este final parece depender del tamaño de la tabla, ya sea que lo obtenga o no. En la prueba a continuación 245/ 246filas se encuentra el punto de corte entre los planos (este también fue el punto de corte entre el ajuste del índice en una página y se convirtió en 2 páginas de hoja y una página raíz).

CREATE TABLE T(A INT,B INT,C INT,D INT)

INSERT INTO T
SELECT TOP (245) 1,2,3,5
FROM master..spt_values v1

CREATE CLUSTERED INDEX IX ON T(A, B, C, D)

SELECT index_level,page_count, record_count
FROM sys.dm_db_index_physical_stats(db_id(),object_id('T'),1,NULL, 'DETAILED')

DECLARE @C1 INT = 3,
        @C2 INT = 4

 SELECT * FROM T WHERE A=1 AND B=2 AND (C=@C1 OR C=@C2) AND D=5

 DROP TABLE T

1 páginas / 245 filas

Este plan tiene una búsqueda A=1 AND B=2con un predicado residual en(C=@C1 OR C=@C2) AND D=5

Plan 1

2 páginas de hoja / 246 filas

Plan 2

En el segundo plan, los operadores adicionales son responsables de eliminar los duplicados del @C1,@C2primero antes de realizar las búsquedas.

La búsqueda en el segundo plan es en realidad una búsqueda de rango entre A=1 AND B=2 AND C > Expr1010y A=1 AND B=2 AND C < Expr1011con un predicado residual activado D=5. Todavía no es una búsqueda de igualdad en las 4 columnas. Puede encontrar más información sobre los operadores de planes adicionales aquí .

Agregar OPTION (RECOMPILE)le permite inspeccionar los valores de los parámetros para duplicados en tiempo de compilación y produce un plan con dos búsquedas de igualdad.

También podrías lograr eso con

;WITH CTE
     AS (SELECT DISTINCT ( C )
         FROM   (VALUES (@C1),
                        (@C2)) V(C))
SELECT CA.*
FROM   CTE
       CROSS APPLY (SELECT *
                    FROM   T
                    WHERE A=1 AND B=2 AND D=5  AND C = CTE.C) CA

Plan 3

Pero en realidad, en este caso de prueba, probablemente sería contraproducente, ya que tener dos búsquedas en el índice de una sola página en lugar de una aumenta el IO lógico.


1
Hice algunas pruebas sobre esta pregunta anoche antes de agregar su primer comentario. Llegué a ver el comportamiento pero no entendí lo que lo estaba causando (@ P3 = @ P4), así que +1 para eso (ya lo hice). Creo que a select .. union select ...también le daría dos búsquedas más el paso adicional de eliminar duplicados del resultado.
Mikael Eriksson

1
@MikaelEriksson - Pero SELECT * FROM T WHERE A=1 AND B=2 AND C=@C1 AND D=5 UNION SELECT * FROM T WHERE A=1 AND B=2 AND C=@C2 AND D=5podría eliminar incorrectamente los duplicados que deberían devolverse. En mi ejemplo, los datos donde perezosamente poblaba todos con el mismo valor que devolvería 1 fila no256
Martin Smith

2
Ah, sí. Y OP incluso especificó que la clave no es única. Lucke me no traté de responder a esta :).
Mikael Eriksson

4

Totalmente de acuerdo con el análisis de Martin. Esta consulta no puede producir una búsqueda en las 4 columnas debido al predicado OR, a menos que (tal vez) con OPTION (RECOMPILE). Supongo que se trata de una consulta de aguja en un pajar, probablemente no desee la sobrecarga adicional.

Qué tal esto:

IF @P3=@P4
    SELECT * FROM T WHERE A=@P1 AND B=@P2 AND C=@P3 AND D=@P5.
ELSE
    SELECT * FROM T WHERE A=@P1 AND B=@P2 AND C=@P3 AND D=@P5.
    UNION ALL
    SELECT * FROM T WHERE A=@P1 AND B=@P2 AND C=@P4 AND D=@P5.

No probé esto, pero la parte else debería dar 2 búsquedas en los 4 valores y una unión barata a través de la concatenación. En caso de que ambas búsquedas terminen en la misma página, no creo que una lectura de página lógica adicional agregue mucho tiempo. Sin embargo, dependiendo de sus datos, las dos búsquedas pueden estar en páginas diferentes.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.