2010-02-19 13 views
11

Estoy tratando de optimizar una consulta SQL compleja y obtener resultados tremendamente diferentes cuando hago cambios aparentemente insignificantes.Problema en la consulta de T-SQL: ¿Por qué el uso de una variable hace la diferencia?

Por ejemplo, esto se lleva a 336 ms para ejecutar:

Declare @InstanceID int set @InstanceID=1; 
With myResults as (
    Select 
     Row = Row_Number() Over (Order by sv.LastFirst), 
     ContactID 
    From DirectoryContactsByContact(1) sv 
    Join ContainsTable(_s_Contacts, SearchText, 'john') fulltext on (fulltext.[Key]=ContactID) 
    Where IsNull(sv.InstanceID,1) = @InstanceID 
    and len(sv.LastFirst)>1 
) Select * From myResults Where Row between 1 and 20; 

Si sustituyo el @InstanceID con un número codificado, se tarda más de 13 segundos (13890 ms) para ejecutar:

Declare @InstanceID int set @InstanceID=1; 
With myResults as (
    Select 
     Row = Row_Number() Over (Order by sv.LastFirst), 
     ContactID 
    From DirectoryContactsByContact(1) sv 
    Join ContainsTable(_s_Contacts, SearchText, 'john') fulltext on (fulltext.[Key]=ContactID) 
    Where IsNull(sv.InstanceID,1) = 1 
    and len(sv.LastFirst)>1 
) Select * From myResults Where Row between 1 and 20; 

En otros casos obtengo el efecto exactamente opuesto: por ejemplo, el uso de una variable @s en lugar del literal 'john' hace que la consulta se ejecute más lentamente en un orden de magnitud.

¿Alguien me puede ayudar a unir esto? ¿Cuándo una variable hace las cosas más rápido y cuándo las hace más lentas?

+0

Se da cuenta de que el uso de '20' TOP y moviendo el ORDER BY de la ROW_NUMBER significa que no es necesario el CTE? –

+0

@OMG: solo si esos números nunca cambian - si quiere obtener las filas 800 - 820, el método CTE es mucho más rápido –

+0

@OMG: @Gabriel tiene razón, esto se usa para entregar resultados paginados, por lo que podría ser 'Fila entre 20 y 40 ', etc. –

Respuesta

2

La causa podría ser que IsNull(sv.InstanceID,1) = @InstanceID es muy selectivo para algunos valores de @InstanceID, pero no muy selectivo para otros. Por ejemplo, podría haber millones de filas con InstanceID = null, por lo que para @InstanceID = 1 un escaneo podría ser más rápido.

Pero si proporciona explícitamente el valor de @InstanceID, SQL Server sabe, en función de las estadísticas de la tabla, si es selectivo o no.

En primer lugar, asegúrese de que sus estadísticas son hasta la fecha:

UPDATE STATISTICS table_or_indexed_view_name 

Entonces, si el problema persiste, comparar el plan de ejecución de consultas para ambos métodos. A continuación, puede aplicar el método más rápido utilizando query hints.

+0

mancha en. Sospecho que las estadísticas están desactualizadas. –

+0

¿Eso significa que las estadísticas también ayudan a predecir cuál podría ser la variable calculada en un procedimiento almacenado? – dsum

0

Con valores codificados, el optimizador sabe en qué se basa al construir el plan de ejecución. Cuando usa variables, intenta "adivinar" el valor y en muchos casos no es el mejor.

Usted puede ayudar a elegir un valor para la optimización de 2 maneras:

  1. "Yo sé mejor", esto obligará a utilizar el valor que usted proporciona.

    OPCIÓN (optimizar (@ InstanceID = 1)) Valor

  2. "ver lo que hago", esto le dará instrucciones a olfatear los valores que pasan y usar la media (o más popular para algunos tipos de datos) de los suministrados a lo largo del tiempo.

    OPCIÓN (optimizar para DESCONOCIDO)

+0

'OPCIÓN (OPTIMIZAR POR DESCONOCIDO)' conducirá exactamente al mismo comportamiento de "adivinación" que el uso de una variable. 'option (recompile)' hará que SQL Server recompile la declaración teniendo en cuenta el valor real de la variable. –

+0

OPCIÓN (OPTIMIZAR POR DESCONOCIDO) habilitará SQL para basar el proceso de adivinación en los valores reales que pasa con el tiempo, por lo que no es exactamente el mismo aunque puede habilitar dicho comportamiento por defecto para todos los SP – root

Cuestiones relacionadas