Differenza tra vista incorporata e clausola WITH?


9

Le viste incorporate ti consentono di selezionare da una sottoquery come se fosse una tabella diversa:

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

Ho visto questo riferito all'uso di termini diversi: viste in linea, clausola WITH, CTE e tabelle derivate. Per me sembra che siano la sintassi specifica del fornitore diverso per la stessa cosa.

È un presupposto sbagliato? Ci sono differenze tecniche / prestazionali tra queste?


5
I nomi "ufficiali" di SQL standard sono Tabella derivata (che Oracle chiama Inline View ) e Common Table Expression (= WITH...). È possibile riscrivere ogni tabella derivata come CTE, ma forse non viceversa (ad esempio CTE ricorsivo o utilizzando il CTE più volte)
dnoeth

Risposte:


8

Esistono alcune importanti differenze tra le viste incorporate (tabelle derivate) e la clausola WITH (CTE) in Oracle. Alcuni di loro sono abbastanza universali, cioè sono applicabili ad altri RDBMS.

  1. WITH può essere usato per creare sottoquery ricorsive, inline view -not (per quanto ne so lo stesso vale per tutti gli RDBMS che supportano CTE)
  2. Sottoquery in WITHÈ più probabile che la clausola venga eseguita fisicamente per prima; in molti casi, la scelta tra WITHvisualizzazione in linea e in linea rende l'ottimizzatore la scelta di piani di esecuzione diversi (suppongo sia specifico del fornitore, forse anche della versione).
  3. Sottoquery in WITH può essere materializzata come una tabella temporanea (non sono a conoscenza se nessun altro fornitore ma Oracle supporta questa funzionalità).
  4. È WITHpossibile fare riferimento più volte alla sottoquery in, in altre sottoquery e nella query principale (vero per la maggior parte dei RDBMS).

MySQL (almeno le ultime versioni di MariaDB) può materializzare tabelle derivate (e persino aggiungere indici).
ypercubeᵀᴹ

3
Vorrei aggiungere che, come vantaggio collaterale, l'uso di CTE è generalmente più leggibile anche per l'uomo.
Joishi Bodio,

@JoishiBodio: Personalmente, sono d'accordo con te, ma la leggibilità è una questione piuttosto soggettiva. Preferirei evitare di menzionarlo
a1ex07

Inoltre, un CTE può fare riferimento a un CTE precedentemente dichiarato. Una tabella derivata non può fare riferimento a una tabella derivata precedentemente dichiarata allo stesso livello a meno che non LATERALvenga utilizzata.
Lennart,

8

Altre risposte coprono abbastanza bene le differenze di sintassi, quindi non entrerò in quello. Invece questa risposta coprirà solo le prestazioni in Oracle.

L'ottimizzatore Oracle può scegliere di materializzare i risultati di un CTE in una tabella temporanea interna. Per farlo, utilizza un'euristica anziché un'ottimizzazione basata sui costi. L'euristica è qualcosa del tipo "Materializza il CTE se non è un'espressione banale e al CTE viene fatto riferimento più di una volta nella query". Ci sono alcune domande per le quali la materializzazione migliorerà le prestazioni. Ci sono alcune domande per le quali la materializzazione ridurrà drasticamente le prestazioni. L'esempio seguente è un po 'inventato ma illustra bene il punto:

Innanzitutto crea una tabella con una chiave primaria che contenga numeri interi compresi tra 1 e 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;

Considera la seguente query che utilizza due tabelle derivate:

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;

Possiamo esaminare questa query e determinare rapidamente che non restituirà alcuna riga. Oracle dovrebbe essere in grado di utilizzare l'indice anche per determinarlo. Sulla mia macchina la query termina quasi istantaneamente con il seguente piano:

buon piano

Non mi piace ripetermi, quindi proviamo la stessa query 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;

Ecco il piano:

cattivo piano

È un piano davvero negativo. Invece di utilizzare l'indice, Oracle materializza 10000 X 10000 = 100000000 righe in una tabella temporanea solo per restituire infine 0 righe. Il costo di questo piano è di circa 6 M, che è molto più alto rispetto all'altra query. Il completamento della query ha richiesto 68 secondi sul mio computer.

Si noti che la query potrebbe non essere riuscita se non vi è memoria o spazio libero sufficienti nel tablespace temporaneo.

Posso usare il INLINEsuggerimento non documentato per impedire all'ottimizzatore di materializzare il 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;

Quella query è in grado di utilizzare l'indice e termina quasi all'istante. Il costo della query è lo stesso di prima, 11. Quindi, per la seconda query, l'euristica utilizzata da Oracle l'ha ottenuta selezionando una query con un costo stimato di 6 M anziché una query con un costo stimato di 11.


1

Per SQL Server, WITH CTEspecifica il set di risultati con nome temporaneo, ma è richiesto solo per il primo CTE. vale a dire

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

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

Ma questa non è una sottoquery o una sottoquery correlata. Ci sono cose che puoi fare con un CTE che non puoi fare con una sottoquery in SQL Server, come aggiornare le tabelle a cui fa riferimento un CTE. Ecco un esempio di aggiornamento di una tabella con un CTE.

Una sottoquery sarebbe qualcosa di simile

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

Oppure una sottoquery correlata è ciò che hai fornito nel tuo OP se dovessi fare riferimento / unire / limitare i risultati in base a a.c1.

Quindi, sicuramente non sono la stessa cosa, anche se in molti casi è possibile utilizzare uno o più di questi metodi per ottenere lo stesso risultato. Dipende solo da quale sia il risultato finale.


1

La differenza principale tra la withclausola e una sottoquery in Oracle è che è possibile fare riferimento a una query all'interno della clausola più volte. Puoi quindi fare alcune ottimizzazioni con esso come trasformarlo in una tabella temporanea usando materializehint. Puoi anche fare query ricorsive con esso facendo riferimento a se stesso all'interno di una withclausola. Non puoi farlo con una vista integrata.

Ulteriori informazioni possono essere trovate qui e qui .


In generale non è richiesto il suggerimento di materializzazione. Per impostazione predefinita, Oracle Optimizer decide se ha senso materializzare o meno il CTE, ma è possibile sovrascrivere la valutazione dell'ottimizzatore con suggerimenti MATERIALIZE. INLINEper il contrario.
Wernfried Domscheit,

@WernfriedDomscheit questo è vero. Ma a volte l'ottimizzatore non sceglie di materializzare il CTE e in tal caso, l'utilizzo di materializehint è un'opzione valida. A volte avevo bisogno di specificarlo quando ottimizzavo query molto complesse in cui sapevo che materializzare il CTE avrebbe beneficiato del piano di esecuzione.
Marko Vodopija,

0

Devi stare attento con i CTE nel server SQL non solo con Oracle, ci sono casi in cui le query hanno prestazioni peggiori quando si usano i CTE rispetto alle sottoquery, alle applicazioni incrociate, ecc.

Come sempre, è importante testare qualsiasi query in varie condizioni di carico per determinare quale funziona meglio.

Simile a @scsimon con Oracle, a volte il server MS SQL non fa ciò che ti aspetti in merito all'utilizzo dell'indice.

Se utilizzerai gli stessi dati più di una volta, i CTE possono essere più utili, se li usi solo una volta, spesso una sottoquery è più veloce in grandi set di dati.

es. seleziona * da (la mia sottoquery) unisciti a qualcos'altro ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.