¿Diferencia entre la vista en línea y la cláusula WITH?

9

Las vistas en línea le permiten seleccionar de una subconsulta como si fuera una tabla diferente:

SELECT
    *
FROM /* Selecting from a query instead of table */
    (
        SELECT
            c1
        FROM
            t1
        WHERE
            c1 > 0
    ) a
WHERE
    a.c1 < 50;

He visto esto referido al uso de diferentes términos: vistas en línea, cláusula WITH, CTE y tablas derivadas. Para mí parece que son diferentes sintaxis específica del proveedor para la misma cosa.

¿Es esta una suposición errónea? ¿Hay alguna diferencia técnica / de rendimiento entre estos?

Kshitiz Sharma
fuente
55
Los nombres "oficiales" de SQL estándar son Tabla derivada (que Oracle nombra Vista en línea ) y Expresión de tabla común (= WITH...). Puede volver a escribir cada tabla derivada como CTE, pero tal vez no al revés (por ejemplo CTE recursiva o el uso de los CTE varias veces)
dnoeth

Respuestas:

8

Existen algunas diferencias importantes entre las vistas en línea (tablas derivadas) y la cláusula WITH (CTE) en Oracle. Algunos de ellos son bastante universales, es decir, son aplicables a otros RDBMS.

  1. WITH se puede usar para crear subconsultas recursivas, vista en línea -no (por lo que sé, lo mismo es para todos los RDBMS que admiten CTE)
  2. La subconsulta en la WITHcláusula es más probable que se ejecute físicamente primero; En muchos casos, elegir entre WITHy la vista en línea hace que el optimizador elija diferentes planes de ejecución (supongo que es específico del proveedor, tal vez incluso de la versión).
  3. La subconsulta WITHpuede materializarse como una tabla temporal (no sé si algún otro proveedor pero Oracle admite esta función).
  4. Se WITHpuede hacer referencia a la subconsulta en varias veces, en otras subconsultas y en la consulta principal (verdadero para la mayoría de los RDBMS).
a1ex07
fuente
MySQL (al menos las últimas versiones de MariaDB) puede materializar tablas derivadas (e incluso agregar índices).
ypercubeᵀᴹ
3
Me gustaría agregar que, como beneficio adicional, el uso de CTE generalmente también es más legible para los humanos.
Joishi Bodio
@JoishiBodio: Personalmente, estoy de acuerdo con usted, pero la legibilidad es un asunto bastante subjetivo. Prefiero evitar mencionarlo
a1ex07
Además, un CTE puede hacer referencia a un CTE previamente declarado. Una tabla derivada no puede hacer referencia a una tabla derivada previamente declarada en el mismo nivel a menos que LATERALse use.
Lennart
8

Otras respuestas cubren las diferencias de sintaxis bastante bien, así que no voy a entrar en eso. En cambio, esta respuesta solo cubrirá el rendimiento en Oracle.

El optimizador de Oracle puede optar por materializar los resultados de un CTE en una tabla temporal interna. Utiliza una heurística para hacer esto en lugar de la optimización basada en costos. La heurística es algo así como "Materializar el CTE si no es una expresión trivial y se hace referencia al CTE más de una vez en la consulta". Hay algunas consultas para las cuales la materialización mejorará el rendimiento. Hay algunas consultas para las cuales la materialización degradará drásticamente el rendimiento. El siguiente ejemplo es un poco artificial pero ilustra bien el punto:

Primero cree una tabla con una clave primaria que contenga enteros del 1 al 10000:

CREATE TABLE N_10000 (NUM_ID INTEGER NOT NULL, PRIMARY KEY (NUM_ID));

INSERT /*+APPEND */ INTO N_10000
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= 10000
ORDER BY LEVEL;

COMMIT;

Considere la siguiente consulta que usa dos tablas derivadas:

SELECT t1.NUM_ID
FROM 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t1
LEFT OUTER JOIN 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Podemos ver esta consulta y determinar rápidamente que no devolverá ninguna fila. Oracle debería poder usar el índice para determinar eso también. En mi máquina, la consulta finaliza casi instantáneamente con el siguiente plan:

buen plan

No me gusta repetirme, así que intentemos la misma consulta con un CTE:

WITH N_10000_CTE AS (
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Aquí está el plan:

mal plan

Ese es un plan realmente malo. En lugar de usar el índice, Oracle materializa 10000 X 10000 = 100000000 filas en una tabla temporal solo para eventualmente devolver 0 filas. El costo de este plan es de alrededor de 6 M, que es mucho más alto que la otra consulta. La consulta tardó 68 segundos en finalizar en mi máquina.

Tenga en cuenta que la consulta podría haber fallado si no hay suficiente memoria o espacio libre en el espacio de tabla temporal.

Puedo usar la INLINEsugerencia no documentada para evitar que el optimizador materialice el CTE:

WITH N_10000_CTE AS (
  SELECT /*+ INLINE */ n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Esa consulta puede usar el índice y finaliza casi al instante. El costo de la consulta es el mismo que antes, 11. Entonces, para la segunda consulta, la heurística utilizada por Oracle resultó en que eligiera una consulta con un costo estimado de 6 M en lugar de una consulta con un costo estimado de 11.

Joe Obbish
fuente
1

Para SQL Server, WITH CTEespecifica el conjunto de resultados con nombre temporal, pero solo se requiere para el primero CTE. es decir

WITH CTE AS (SELECT .... FROM), 
CTE2 AS (SELECT .... FROM)

SELECT CTE.Column, CTE2.Column
FROM CTE
INNER JOIN CTE2 on CTE.Column = CTE2.Column

Pero esto no es una subconsulta o una subconsulta correlacionada. Hay cosas que puede hacer con un CTE que no puede hacer con una subconsulta en SQL Server, como actualizar las Tablas a las que se hace referencia en un CTE. Aquí hay un ejemplo de actualización de una tabla con un CTE.

Una subconsulta sería algo así como

SELECT
   C1,
   (SELECT C2 FROM SomeTable) as C2
FROM Table

O una subconsulta correlacionada es lo que ha proporcionado en su OP si tuviera que hacer referencia / unirse / limitar sus resultados según a.c1.

Por lo tanto, definitivamente no son lo mismo, aunque en muchos casos podría usar uno o más de estos métodos para lograr el mismo resultado. Solo depende de cuál sea el resultado final.

scsimon
fuente
1

La principal diferencia entre la withcláusula y una subconsulta en Oracle es que puede hacer referencia a una consulta dentro de la cláusula varias veces. A continuación, puede hacer algunas optimizaciones con él, como convertirlo en una tabla temporal con una materializesugerencia. También puede hacer consultas recursivas con ella haciendo referencia a sí misma dentro de una withcláusula. No puede hacer eso con una vista en línea.

Se puede encontrar más información aquí y aquí .

Marko Vodopija
fuente
En general no se requiere materializar pista. De forma predeterminada, el optimizador de Oracle decide si tiene sentido materializar el CTE o no, pero puede sobrescribir la evaluación del optimizador con una pista MATERIALIZEresp. INLINEpor lo contrario
Wernfried Domscheit
@WernfriedDomscheit eso es cierto. Pero a veces el optimizador no elige materializar el CTE y, en ese caso, usar la materializesugerencia es una opción válida. A veces necesitaba especificarlo al optimizar consultas muy complejas donde sabía que materializar el CTE beneficiaría el plan de ejecución.
Marko Vodopija
0

Debe tener cuidado con los CTE en el servidor SQL, no solo con Oracle, hay casos en los que las consultas funcionan mucho peor cuando se usan CTE en comparación con las subconsultas, la aplicación cruzada, etc.

Como siempre, es importante probar cualquier consulta en varias condiciones de carga para determinar cuál funciona mejor.

Al igual que @scsimon con Oracle, a veces el servidor MS SQL no hace lo que espera con respecto al uso del índice.

Si va a usar los mismos datos más de una vez, los CTE pueden ser más útiles, si solo los usa una vez, a menudo una subconsulta es más rápida en grandes conjuntos de datos.

por ejemplo, seleccione * de (mi subconsulta) unirse a otra cosa ...

Justin
fuente