2010-03-05 6 views
6

Tengo una mesa ItemValue completa de los datos en un servidor SQL 2005 se ejecuta en modo de compatibilidad de 2000, que se ve algo como (Es una tabla los valores definidos por el usuario):Optimización de SQL: cambios en el plan de ejecución en función del valor de restricción. ¿Por qué?

ID ItemCode  FieldID Value 
-- ---------- ------- ------ 
1 abc123    1 D 
2 abc123    2 287.23 
4 xyz789    1 A 
5 xyz789    2 3782.23 
6 xyz789    3 23 
7 mno456    1 W 
9 mno456    3 45 
           ... and so on. 

fieldID proviene de la Itemfield tabla:

ID FieldNumber DataFormatID Description ... 
-- ----------- ------------ ----------- 
1    1    1 Weight class 
2    2    4 Cost 
3    3    3 Another made up description 
.    .    x xxx 
.    .    x xxx 
.    .    x xxx 
x    91 (we have 91 user-defined fields) 

Porque puedo no pivota en el modo 2000, estamos atrapados construir una consulta fea utilizando casos y GROUP BY para obtener los datos que debe buscar la forma en que deberían para s ome aplicaciones de legado, que es:

ItemNumber Field1 Field2 Field3 .... Field51 
---------- ------ ------- ------ 
    abc123 D  287.23 NULL 
    xyz789 A  3782.23 23 
    mno456 W  NULL  45 

Usted puede ver sólo tenemos esta tabla para mostrar valores de hasta el 51 UDF. Aquí está la consulta:

SELECT 
    iv.ItemNumber, 
    ,MAX(CASE WHEN f.FieldNumber = 1 THEN iv.[Value] ELSE NULL END) [Field1] 
    ,MAX(CASE WHEN f.FieldNumber = 2 THEN iv.[Value] ELSE NULL END) [Field2] 
    ,MAX(CASE WHEN f.FieldNumber = 3 THEN iv.[Value] ELSE NULL END) [Field3] 
     ... 
    ,MAX(CASE WHEN f.FieldNumber = 51 THEN iv.[Value] ELSE NULL END) [Field51] 
FROM ItemField f 
LEFT JOIN ItemValue iv ON f.ID = iv.FieldID 
WHERE f.FieldNumber <= 51 
GROUP BY iv.ItemNumber 

Cuando el FieldNumber restricción es < = 51, el plan de ejecución es algo como:

SELECT <== Computer Scalar <== Stream Aggregate <== Sort (Cost: 70%) <== Hash Match <== (Clustered Index Seek && Table Scan)

y es rápido! Puedo retirar más de 100.000 registros en aproximadamente un segundo, lo que se adapta a nuestras necesidades.

Sin embargo, si tuviéramos más UDF y yo cambiamos la restricción a cualquier cosa por encima (sí, yo los probé uno a uno) o si elimino por completo, pierdo el tipo en el plan de ejecución, y se reemplaza con un montón de bloques de Paralelismo que reúnen, reparten y distribuyen transmisiones, y todo es lento (30 segundos incluso por solo 1 registro).

FieldNumber tiene una, índice único agrupado, y es parte de la clave primaria de material compuesto con el columna ID (índice no agrupado) en el tabla Itemfield. ID y ItemNumber columnas del ItemValue de mesa hacen una PK, y hay un índice no agrupado adicional en la columna de la ItemNumber.

¿Cuál es el razonamiento detrás de esto? ¿Por qué el cambio de mi restricción de entero simple cambia todo el plan de ejecución?

Y si está a la altura ... ¿qué harías de manera diferente? Hay una actualización SQL planificada para un par de meses a partir de ahora, pero necesito solucionar este problema antes de eso.

+0

Lo que haría de manera diferente no es usar esa estructura de tabla. Es mucho mejor usar una estructura con campos definidos para el 99% de las necesidades de datos que se pueden descifrar de antemano que para usar una tabla EAV. Dicho sea de paso, el modo de compatilidad no impide el uso de nuevas funciones, sino que permite el uso de funciones que ya no están permitidas. Sin embargo, si está desarrollando una base de datos de 2005 con una base de datos de producción 2000, es mejor evitar las nuevas características. – HLGEM

Respuesta

4

SQL Server es lo suficientemente inteligente como para tomar en cuenta CHECK restricciones al optimizar las consultas.

Su f.FieldNumber <= 51 está optimizado y el optimizador ve que se deben unir las dos tablas (lo que se hace mejor con un HASH JOIN).

Si no tiene la restricción, el motor debe verificar la condición y lo más probable es que utilice el recorrido del índice para hacerlo. Esto puede ser más lento.

¿Podría publicar los planes completos para las consultas? Simplemente ejecute SET SHOWPLAN_TEXT ON y luego las consultas.

Actualización:

¿Cuál es el razonamiento detrás de esto? ¿Por qué el cambio de mi restricción de entero simple cambia todo el plan de ejecución?

Si por una restricción quiere decir la condición WHERE, esta es probablemente la otra cosa.

Las operaciones de conjunto (eso es lo que hace SQL) no tienen el algoritmo más eficiente: la eficacia de cada algoritmo depende en gran medida de la distribución de datos en los conjuntos.

Digamos que para tomar un subconjunto (eso es lo que hace la cláusula WHERE) puede encontrar el rango de registro en el índice y usar los punteros de registro de índice para localizar las filas de datos en la tabla, o simplemente escanear todos los registros la tabla y filtrarlos usando la condición WHERE.

eficiencia de la operación anterior es m × const, la de la última es n, donde m es el número de registro que satisface la condición, n es el número total de registros en la tabla y const > 1.

Esto significa que para valores más grandes de m el barrido completo es más eficiente.

SQL Server es consciente de eso y cambia los planes de ejecución según las constantes que afectan a la distribución de datos en las operaciones establecidas.

Para hacerlo, SQL Server mantiene estadísticas: histogramas agregados de la distribución de datos en cada columna indexada y los utiliza para generar los planes de consulta.

Así que cambiar el número entero en la condición WHERE afecta el tamaño y la distribución de datos de los conjuntos subyacentes y hace que SQL Server reconsidere los algoritmos que mejor se adaptan para trabajar con los conjuntos de ese tamaño y diseño.

+0

No veo ninguna mención de una restricción CHECK en OP ... –

+0

Publicaré los planes, pero tengo que hacer un par de cambios para que mis tablas reales no den ninguna información de identificación (mi ejemplo está adulterado). –

+0

@Remus: Cuando lo leí, creí que * cuando la restricción FieldNumber es '<= 51' * era sobre una restricción' CHECK', pero probablemente tienes razón, el @op mucho significaba la condición 'WHERE'. – Quassnoi

0

que es reemplazado con un montón de paralelismo bloques

Prueba esto:

SELECT 
    iv.ItemNumber, 
    ,MAX(CASE WHEN f.FieldNumber = 1 THEN iv.[Value] ELSE NULL END) [Field1] 
    ,MAX(CASE WHEN f.FieldNumber = 2 THEN iv.[Value] ELSE NULL END) [Field2] 
    ,MAX(CASE WHEN f.FieldNumber = 3 THEN iv.[Value] ELSE NULL END) [Field3] 
     ... 
    ,MAX(CASE WHEN f.FieldNumber = 51 THEN iv.[Value] ELSE NULL END) [Field51] 
FROM ItemField f 
LEFT JOIN ItemValue iv ON f.ID = iv.FieldID 
WHERE f.FieldNumber <= 51 
GROUP BY iv.ItemNumber 
OPTION (Maxdop 1) 

Mediante el uso de la opción (MAXDOP 1), esto se debe evitar que el parellelism en el plan de ejecución.

+0

Así que esto realmente funciona, pero no puede poner OPCIÓN en una vista cuando lo necesito. –

0

En 66 usted está alcanzando algún umbral interno de estimación de costos que decide que es mejor usar un plan frente al otro. Lo que ese umbral es y por qué sucede no es realmente importante. Tenga en cuenta que su consulta difiere con cada valor FieldNumber, ya que no solo está cambiando WHERE: también cambia los campos proyectados pseudo-'pivot '.

Ahora no sé todos los detalles de su mesa y sus consultas e inserto patrón/actualizar/borrar /, pero para la consulta particular que envió la estructura de índice agrupado adecuada para la tabla ItemValue es la siguiente:

CREATE CLUSTERED INDEX [cdxItemValue] ON ItemValue (FieldID, ItemNumber); 

Esta estructura elimina la necesidad de ordenar de forma intermedia los resultados de esta consulta 'pivote'.

Cuestiones relacionadas