¿Por qué cambiar el orden de la columna de unión declarada introduce una ordenación?

40

Tengo dos tablas con columnas de clave con nombres, tipos e índices idénticos. Uno de ellos tiene un índice agrupado único , el otro tiene un índice no único .

La configuración de prueba

Script de configuración, que incluye algunas estadísticas realistas:

DROP TABLE IF EXISTS #left;
DROP TABLE IF EXISTS #right;

CREATE TABLE #left (
    a       char(4) NOT NULL,
    b       char(2) NOT NULL,
    c       varchar(13) NOT NULL,
    d       bit NOT NULL,
    e       char(4) NOT NULL,
    f       char(25) NULL,
    g       char(25) NOT NULL,
    h       char(25) NULL
    --- and a few other columns
);

CREATE UNIQUE CLUSTERED INDEX IX ON #left (a, b, c, d, e, f, g, h)

UPDATE STATISTICS #left WITH ROWCOUNT=63800000, PAGECOUNT=186000;

CREATE TABLE #right (
    a       char(4) NOT NULL,
    b       char(2) NOT NULL,
    c       varchar(13) NOT NULL,
    d       bit NOT NULL,
    e       char(4) NOT NULL,
    f       char(25) NULL,
    g       char(25) NOT NULL,
    h       char(25) NULL
    --- and a few other columns
);

CREATE CLUSTERED INDEX IX ON #right (a, b, c, d, e, f, g, h)

UPDATE STATISTICS #right WITH ROWCOUNT=55700000, PAGECOUNT=128000;

La repro

Cuando uní estas dos tablas en sus claves de agrupación, espero una unión MERGE de uno a muchos, así:

SELECT *
FROM #left AS l
LEFT JOIN #right AS r ON
    l.a=r.a AND
    l.b=r.b AND
    l.c=r.c AND
    l.d=r.d AND
    l.e=r.e AND
    l.f=r.f AND
    l.g=r.g AND
    l.h=r.h
WHERE l.a='2018';

Este es el plan de consulta que quiero:

Esto es lo que quiero.

(No importa las advertencias, tienen que ver con las estadísticas falsas).

Sin embargo, si cambio el orden de las columnas en la unión, así:

SELECT *
FROM #left AS l
LEFT JOIN #right AS r ON
    l.c=r.c AND     -- used to be third
    l.a=r.a AND     -- used to be first
    l.b=r.b AND     -- used to be second
    l.d=r.d AND
    l.e=r.e AND
    l.f=r.f AND
    l.g=r.g AND
    l.h=r.h
WHERE l.a='2018';

... esto pasa:

El plan de consulta después de cambiar el orden de columna declarado en la unión.

El operador Ordenar parece ordenar las secuencias de acuerdo con el orden declarado de la unión, es decir c, a, b, d, e, f, g, h, que agrega una operación de bloqueo a mi plan de consulta.

Cosas que he visto

  • Intenté cambiar las columnas a los NOT NULLmismos resultados.
  • La tabla original se creó con ANSI_PADDING OFF, pero crearla con ANSI_PADDING ONno afecta a este plan.
  • Intenté un en INNER JOINlugar de LEFT JOIN, sin cambios.
  • Lo descubrí en una empresa SP2 2014, creé una reproducción en un desarrollador 2017 (CU actual).
  • Eliminar la cláusula WHERE en la columna de índice inicial genera el buen plan, pero afecta los resultados ... :)

Finalmente, llegamos a la pregunta.

  • ¿Es esto intencional?
  • ¿Puedo eliminar el tipo sin cambiar la consulta (que es el código del proveedor, por lo que realmente prefiero no ...). Puedo cambiar la tabla y los índices.
Daniel Hutmacher
fuente

Respuestas:

28

¿Es esto intencional?

Es por diseño, sí. Desafortunadamente, la mejor fuente pública para esta afirmación se perdió cuando Microsoft retiró el sitio de comentarios Connect, borrando muchos comentarios útiles de los desarrolladores del equipo de SQL Server.

De todos modos, el diseño actual del optimizador no busca activamente evitar tipos innecesarios per se . Esto se encuentra con mayor frecuencia con las funciones de ventanas y similares, pero también se puede ver con otros operadores que son sensibles al orden y, en particular, al orden conservado entre operadores.

Sin embargo, el optimizador es bastante bueno (en muchos casos) para evitar una clasificación innecesaria, pero este resultado normalmente ocurre por otras razones que no son agresivamente probar diferentes combinaciones de orden. En ese sentido, no se trata tanto del "espacio de búsqueda" como de las complejas interacciones entre las características del optimizador ortogonal que han demostrado aumentar la calidad del plan general a un costo aceptable.

Por ejemplo, la ordenación a menudo se puede evitar simplemente haciendo coincidir un requisito de pedido (por ejemplo, de nivel superior ORDER BY) con un índice existente. Trivialmente en su caso, eso podría significar agregar, ORDER BY l.a, l.b, l.c, l.d, l.e, l.f, l.g, l.h;pero esto es una simplificación excesiva (e inaceptable porque no desea cambiar la consulta).

Más generalmente, cada grupo de notas puede estar asociado con las propiedades requeridas o deseadas, que pueden incluir el pedido de entradas. Cuando no hay una razón obvia para hacer cumplir un orden en particular (por ejemplo, para satisfacer ORDER BYo asegurar resultados correctos de un operador físico sensible al orden), hay un elemento de "suerte" involucrado. Escribí más sobre los detalles de eso en lo que respecta a la combinación de combinación (en modo unión o combinación) en Evitar clasificaciones con concatenación de combinación de combinación . Gran parte de eso va más allá del área de superficie admitida del producto, así que trátelo como informativo y sujeto a cambios.

En su caso particular, sí, puede ajustar la indexación como jadarnel27 sugiere para evitar el tipo; aunque hay pocas razones para preferir una fusión, únete aquí. También puede insinuar una elección entre combinación física de hash o bucle con el OPTION(HASH JOIN, LOOP JOIN)uso de una Guía de planes sin cambiar la consulta, dependiendo de su conocimiento de los datos, y la compensación entre el mejor, el peor y el rendimiento promedio del caso.

Finalmente, como curiosidad, tenga en cuenta que los tipos se pueden evitar con una ORDER BY l.bcombinación simple , a costa de una combinación de muchos a muchos potencialmente menos eficiente b, sola, con un residuo complejo. Menciono esto principalmente como una ilustración de la interacción entre las características del optimizador que mencioné anteriormente, y la forma en que los requisitos de nivel superior pueden propagarse.

Paul White dice GoFundMonica
fuente
19

¿Puedo eliminar el tipo sin cambiar la consulta (que es el código del proveedor, por lo que realmente prefiero no ...). Puedo cambiar la tabla y los índices.

Si puede cambiar los índices, al cambiar el orden del índice #rightpara que coincida con el orden de los filtros en la unión, se elimina el orden (para mí):

CREATE CLUSTERED INDEX IX ON #right (c, a, b, d, e, f, g, h)

Sorprendentemente (para mí, al menos), esto da como resultado que ninguna consulta termine en una especie.

¿Es esto intencional?

Mirando la salida de algunos indicadores de rastreo extraños , hay una diferencia interesante en la estructura final de Memo:

captura de pantalla de la estructura final del memo para cada consulta

Como puede ver en el "Grupo raíz" en la parte superior, ambas consultas tienen la opción de usar una combinación de combinación como la operación física principal para ejecutar esta consulta.

Buena consulta

La unión sin la ordenación es impulsada por el grupo 29 opción 1 y el grupo 31 opción 1 (cada uno de los cuales son escaneos de rango en los índices involucrados). Se filtra por el grupo 27 (no se muestra), que es la serie de operaciones de comparación lógica que filtran la unión.

Mala consulta

El que tiene la clasificación está dirigido por las (nuevas) opciones 3 que tiene cada uno de esos dos grupos (29 y 31). La opción 3 realiza una clasificación física de los resultados de los escaneos de rango mencionados anteriormente (opción 1 de cada uno de esos grupos).

¿Por qué?

Por alguna razón, la opción de usar 29.1 y 31.1 directamente como fuentes para la combinación de fusión ni siquiera está disponible para el optimizador en la segunda consulta. De lo contrario, creo que se enumeraría en el grupo raíz entre las otras opciones. Si estuviera disponible, definitivamente los elegiría entre las operaciones de clasificación masivamente más caras.

Solo puedo concluir que:

  • esto es un error (o más probablemente una limitación) en el algoritmo de búsqueda del optimizador
    • Al cambiar los índices y las combinaciones para que solo tengan 5 teclas, se elimina el orden para la segunda consulta (6, 7 y 8 teclas tienen el orden).
    • Esto implica que el espacio de búsqueda con 8 teclas es tan grande que el optimizador simplemente no tiene tiempo para identificar la solución no ordenada como una opción viable antes de que termine antes con la razón "plan suficientemente bueno encontrado"
    • me parece un poco defectuoso que el orden de las condiciones de unión influye tanto en el proceso de búsqueda del optimizador, pero realmente eso está un poco sobre mi cabeza
  • Se requiere la clasificación para garantizar la exactitud de los resultados.
    • esto parece poco probable, ya que la consulta puede ejecutarse sin el orden cuando hay menos claves, o las claves se especifican en un orden diferente

Espero que alguien pueda venir y explicar por qué se requiere el tipo, pero pensé que la diferencia en el edificio de Memo era lo suficientemente interesante como para publicar como respuesta.

Josh Darnell
fuente
1
Creo que su comentario sobre el espacio de búsqueda es realmente el caso aquí. Para usar solo los índices, el optimizador tiene que verificar que sean suficientes para las condiciones, después de las 5 teclas hay demasiadas posibilidades para verificar antes de que tenga que retroceder. Sería curioso, si se enumeraran todas las combinaciones de pedidos de la consulta, en cuántas tendría éxito el optimizador frente al retroceso
Mr.Mindor
Y sí, la inconsistencia parece un poco defectuosa, pero probablemente depende totalmente del algoritmo utilizado para verificar que los índices son suficientes. Si se probaron todas las combinaciones, probablemente podría ver el patrón en los resultados y determinar qué algoritmo se utiliza. Apuesto a que está escrito para funcionar de manera óptima para los casos de uso más típicos. Puede existir una alternativa que sea capaz de encontrar la solución de 8 claves de manera confiable dentro del límite de tiempo, pero es más lenta que la solución actual cuando hay menos de 3 a 4 claves.
Mr.Mindor