10

Estoy construyendo una vista en SQL Server 2000 (y 2005) y me he dado cuenta de que el orden de las instrucciones de combinación afecta en gran medida el plan de ejecución y la velocidad de la consulta.¿Por qué el orden de las cláusulas de unión afecta el plan de consulta en SQL Server?

select  sr.WTSASessionRangeID, 
      -- bunch of other columns 
from  WTSAVW_UserSessionRange us 
inner join WTSA_SessionRange sr on sr.WTSASessionRangeID = us.WTSASessionRangeID 
left outer join WTSA_SessionRangeTutor srt on srt.WTSASessionRangeID = sr.WTSASessionRangeID 
left outer join WTSA_SessionRangeClass src on src.WTSASessionRangeID = sr.WTSASessionRangeID 
left outer join WTSA_SessionRangeStream srs on srs.WTSASessionRangeID = sr.WTSASessionRangeID 
--left outer join MO_Stream ms on ms.MOStreamID = srs.MOStreamID 
left outer join WTSA_SessionRangeEnrolmentPeriod srep on srep.WTSASessionRangeID = sr.WTSASessionRangeID 
left outer join WTSA_SessionRangeStudent stsd on stsd.WTSASessionRangeID = sr.WTSASessionRangeID 
left outer join WTSA_SessionSubrange ssr on ssr.WTSASessionRangeID = sr.WTSASessionRangeID 
left outer join WTSA_SessionSubrangeRoom ssrr on ssrr.WTSASessionSubrangeID = ssr.WTSASessionSubrangeID 
left outer join MO_Stream ms on ms.MOStreamID = srs.MOStreamID 

En SQL Server 2000, la consulta anterior genera constantemente un plan de costo 946. Si elimine el comentario MO_Stream unirse en el medio de la consulta y comente la otra en la inferior, el costo se reduce a 263. La velocidad de ejecución cae en consecuencia. Siempre pensé que el optimizador de consultas interpretaría la consulta de manera apropiada sin considerar el orden de unión, pero parece que el orden es importante.

Por lo tanto, dado que la orden hace parece importar, ¿hay alguna estrategia de unión que deba seguir para escribir consultas más rápidas?

(Por cierto, en SQL Server 2005, con datos casi idénticos, los costos del plan de consulta fueron 0,675 y 0,631, respectivamente.)

Editar: En SQL Server 2000, aquí están las estadísticas perfiladas:

  • 946-cost query: 9094ms CPU, 5121 reads, 0 writes, 10123ms duration
  • 263-cost query: 172ms CPU, 7477 reads, 0 writes, 170ms duration

Edit: Aquí está la estructura lógica de las tablas.

SessionRange ---+--- SessionRangeTutor 
       |--- SessionRangeClass 
       |--- SessionRangeStream --- MO_Stream 
       |--- SessionRangeEnrolmentPeriod 
       |--- SessionRangeStudent 
       +----SessionSubrange --- SessionSubrangeRoom 

Editar: Gracias a Alex y GBN para mí apuntando en la dirección correcta. También encontré this question.

Aquí está la nueva consulta:

select sr.WTSASessionRangeID // + lots of columns 

from WTSAVW_UserSessionRange us 
inner join WTSA_SessionRange sr on sr.WTSASessionRangeID = us.WTSASessionRangeID 
left outer join WTSA_SessionRangeTutor srt on srt.WTSASessionRangeID = sr.WTSASessionRangeID 
left outer join WTSA_SessionRangeClass src on src.WTSASessionRangeID = sr.WTSASessionRangeID 
left outer join WTSA_SessionRangeEnrolmentPeriod srep on srep.WTSASessionRangeID = sr.WTSASessionRangeID 
left outer join WTSA_SessionRangeStudent stsd on stsd.WTSASessionRangeID = sr.WTSASessionRangeID 

// SessionRangeStream is a many-to-many mapping table between SessionRange and MO_Stream 
left outer join (
    WTSA_SessionRangeStream srs 
    inner join MO_Stream ms on ms.MOStreamID = srs.MOStreamID 
) on srs.WTSASessionRangeID = sr.WTSASessionRangeID 

// SessionRanges MAY have Subranges and Subranges MAY have Rooms 
left outer join (
    WTSA_SessionSubrange ssr  
    left outer join WTSA_SessionSubrangeRoom ssrr on ssrr.WTSASessionSubrangeID = ssr.WTSASessionSubrangeID 
) on ssr.WTSASessionRangeID = sr.WTSASessionRangeID 

costo SQLServer2000: 24.9

Respuesta

6

Tengo que estar en desacuerdo con todas las respuestas anteriores, y la razón es simple: si cambia el orden de su combinación izquierda, sus consultas son lógicamente diferentes y, como tales, producen diferentes conjuntos de resultados. Véalo usted mismo:

SELECT 1 AS a INTO #t1 
UNION ALL SELECT 2 
UNION ALL SELECT 3 
UNION ALL SELECT 4; 

SELECT 1 AS b INTO #t2 
UNION ALL SELECT 2; 

SELECT 1 AS c INTO #t3 
UNION ALL SELECT 3; 

SELECT a, b, c 
FROM #t1 LEFT JOIN #t2 ON #t1.a=#t2.b 
    LEFT JOIN #t3 ON #t2.b=#t3.c 
ORDER BY a; 

SELECT a, b, c 
FROM #t1 LEFT JOIN #t3 ON #t1.a=#t3.c 
    LEFT JOIN #t2 ON #t3.c=#t2.b 
ORDER BY a; 

a   b   c 
----------- ----------- ----------- 
1   1   1 
2   2   NULL 
3   NULL  NULL 
4   NULL  NULL 

(4 row(s) affected) 

a   b   c 
----------- ----------- ----------- 
1   1   1 
2   NULL  NULL 
3   NULL  3 
4   NULL  NULL 
+0

Depende de la estructura de las tablas. Estás en lo correcto para el caso T1-T2, T2-T3. En mi situación, es T1-T2, T1-T3. – geofftnz

+0

@geofftnz: ver mi respuesta. Usted * no * T1-T2, T1-T3 – gbn

2

Obviamente, el optimizador de SQL Server 2005 es mucho mejor que el 2000 uno de SQL Server.

Sin embargo, hay mucha verdad en su pregunta. Las uniones externas harán que la ejecución varíe salvajemente según el orden (las combinaciones internas tienden a optimizarse a la ruta más eficiente, pero nuevamente, el orden importa). Si lo piensas bien, a medida que construyes uniones hacia la izquierda, necesitas descubrir qué diablos está a la izquierda. Como tal, cada unión debe calcularse antes de que se pueda hacer cualquier otra unión. Se vuelve secuencial, y no paralelo. Ahora, obviamente, hay cosas que puede hacer para combatir esto (como índices, vistas, etc.). Pero, el punto es: la tabla necesita saber qué hay a la izquierda antes de que pueda hacer una combinación externa izquierda. Y si continúa agregando combinaciones, obtendrá cada vez más abstracción de lo que está exactamente a la izquierda (especialmente si usa tablas combinadas como la tabla de la izquierda).

Con uniones internas, sin embargo, puede paralelizarlas un poco, por lo que hay una diferencia menos dramática en lo que se refiere a la orden.

+0

Gracias por el aporte Eric - Voy a hacer más arreglos para ver si puedo bajar el costo aún más. Hay índices en todas las columnas usadas para unir.Desafortunadamente, la naturaleza de los datos significa que tengo que usar uniones externas para este. – geofftnz

+0

"Obviamente, el optimizador de SQL Server 2005 es mucho mejor que el de SQL Server 2000". Eso es un eufemismo. No solo es débil el optimizador de consultas de SQL Server 2000 (junto con el resto de la edición de 2000, en mi opinión), a menudo parece sobre optimizar, haciendo suposiciones falsas que dan como resultado datos hilarantemente incorrectos. Una unión de vistas, o una vista que contenga una unión, o una vista que contenga una vista (!) Puede desencadenar esta idiotez una vez que cruce un cierto umbral de complejidad. Nunca he sido capaz de identificar la naturaleza del problema exactamente, pero a menudo me ha mordido. – WCWedin

1

depende de cuáles de los campos de unión están indexados; si tiene que escanear la tabla del primer campo, pero usa un índice en el segundo, es lento. Si su primer campo de unión es un índice, será más rápido.Supongo que 2005 lo optimiza mejor determinando los campos indexados y realizando los primeros

2

Una estrategia general para optimizar las consultas que contienen JOINs es mirar su modelo de datos y los datos y tratar de determinar qué JOIN reducirá el número de registros eso debe ser considerado de la manera más rápida. Cuantos menos registros se deben considerar, más rápido se ejecutará la consulta. El servidor generalmente producirá un mejor plan de consulta también.

Junto con la optimización por encima de asegurarse de que todos los campos utilizados en las combinaciones estén indexados

1

En DevConnections hace unos años una sesión en el rendimiento de SQL Server indican que (a) el fin de combinaciones externas sí importa, y (b) cuando una consulta tiene muchas combinaciones, no las verá todas antes de realizar una determinación en un plan. Si sabe que tiene uniones que ayudarán a acelerar una consulta, deberían estar al principio de la lista FROM (si puede).

2

Probablemente su consulta sea incorrecta. Alex es correcto Eric también puede estar correcto, pero la consulta es incorrecta.

Lets' tomar este subconjunto:

WTSA_SessionRange sr 
left outer join 
WTSA_SessionSubrange ssr on ssr.WTSASessionRangeID = sr.WTSASessionRangeID 
left outer join 
WTSA_SessionSubrangeRoom ssrr on ssrr.WTSASessionSubrangeID = ssr.WTSASessionSubrangeID 

Usted está uniendo a WTSA_SessionSubrangeRoom WTSA_SessionSubrange. Puede que no tenga filas de WTSA_SessionSubrange.

la unión debe ser la siguiente:

WTSA_SessionRange sr 
left outer join 
(SELECT WTSASessionRangeID, columns I need 
FROM 
    WTSA_SessionSubrange ssr 
    left outer join 
    WTSA_SessionSubrangeRoom ssrr on ssrr.WTSASessionSubrangeID = ssr.WTSASessionSubrangeID 
) foo on foo.WTSASessionRangeID = sr.WTSASessionRangeID 

Esta es la razón por la orden de combinación está afectando a los resultados porque es una consultadiferente, de forma declarativa hablando.

También necesitaría cambiar el MO_Stream y el WTSA_SessionRangeStream unirse también.

+0

¿Qué situaciones darían lugar a que estas dos consultas devolvieran resultados diferentes? – geofftnz

+0

Exactamente como Alex demostró ... – gbn

+0

Pero en la situación T1-T2 T2-T3, si había una relación de clave externa entre T2 y T3 de modo que una fila en T3 no puede existir sin hacer referencia a una fila en T2, todavía importa ? – geofftnz

3

La orden de unión hace una diferencia en la consulta resultante. Esto está documentado en BOL en la documentación para FROM:

<joined_table>

Es un conjunto de resultados que es el producto de dos o más tablas. Para uniones múltiples, use paréntesis para cambiar el orden natural de las uniones.

Puede alterar el orden de unión usando paréntesis alrededor de las uniones (BOL lo muestra en la sintaxis en la parte superior de los documentos, pero es fácil pasar por alto).

Esto se conoce como comportamiento quiástico. También puede usar la sugerencia de consulta OPTION (FORCE ORDER) para forzar una orden de unión específica, pero esto puede dar como resultado lo que se llama "planes espesos" que puede no ser el más óptimo para la consulta que se está ejecutando.

Cuestiones relacionadas