Sintassi di INNER JOIN nidificata all'interno di OUTER JOIN rispetto ai risultati della query


10

TLDR; Se dai un'occhiata ai 2 piani di esecuzione, c'è una risposta facile a quale è meglio? Non ho intenzionalmente creato indici, quindi è più facile vedere cosa sta succedendo.

In seguito alla mia domanda precedente in cui abbiamo riscontrato differenze di prestazioni della query tra diversi stili di join (ovvero nidificati rispetto a quelli tradizionali), mi sono reso conto che la sintassi nidificata modifica anche il comportamento della query. Considera le seguenti 2 query.

SELECT  a.*, m.*, n.*
FROM    dbo.Autos a
LEFT JOIN dbo.Models m
  JOIN dbo.Manufacturers n  -- <-- Nested INNER JOIN
  ON n.ManufacturerID = m.ManufacturerID
ON m.ModelID = a.ModelID

inserisci qui la descrizione dell'immagine

Non è necessario che i produttori si uniscano per includere una riga automatica con un ModelID NON presente nella tabella Modelli.

inserisci qui la descrizione dell'immagine

Usando la sintassi tradizionale, dobbiamo cambiare il join in Manufactures in un join esterno, in questo modo ... ma questo cambia il piano di query.

SELECT a.*, m.*, n.*
FROM dbo.Autos a
LEFT JOIN dbo.Models m
ON m.ModelID = a.ModelID
LEFT JOIN dbo.Manufacturers n -- <-- Now LEFT OUTER JOIN
ON n.ManufacturerID = m.ManufacturerID

inserisci qui la descrizione dell'immagine

Risposte:


12

Se dai un'occhiata ai 2 piani di esecuzione, c'è una risposta facile a quale è meglio? Non ho intenzionalmente creato indici, quindi è più facile vedere cosa sta succedendo.

Il secondo piano ha un costo stimato inferiore, quindi in questo senso limitato è "migliore".

I set di dati sono così piccoli che l'ottimizzatore non ha trascorso molto tempo a cercare alternative. La prima forma della query capita di trovare un piano usando hash join e uno spool di tabella all'inizio. Il costo stimato di quel piano è così basso che l'ottimizzatore non si preoccupa di cercare qualcosa di meglio.

La seconda forma della query capita di trovare un piano usando solo loop nidificati join esterni all'inizio del processo di ricerca, e di nuovo l'ottimizzatore decide che il piano è abbastanza buono. Accade così che si stima che questo piano sia più economico.

Detto questo (come menzionato nei commenti alle domande) le due query non sono semanticamente identiche. Questo potrebbe non essere importante per te se puoi garantire che i risultati saranno sempre gli stessi per tutti i possibili stati futuri del tuo database, ma l'ottimizzatore non può fare questo presupposto. Produce sempre e solo piani che sono garantiti per produrre gli stessi risultati specificati da SQL, in ogni circostanza.

Mi sono reso conto che la sintassi nidificata modifica anche il comportamento della query.

La "sintassi nidificata" è solo un aspetto dell'intera specifica della sintassi del join ANSI. Per abilitare una specifica logica completa per modelli di join più complessi, la specifica consente parentesi (opzionali) e FROMsottoquery di clausole.

La query può essere scritta usando la stessa sintassi ANSI usando le parentesi:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN
(
    dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) ON M.ModelID = A.ModelID;

Questa forma mostra chiaramente che il requisito logico è aderire sinistra dal Autosal risultato di interni unirsi Manufacturersa Models. Omettendo le parentesi opzionali si ottiene il modulo che si chiama 'nidificato':

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
    ON M.ModelID = A.ModelID;

Questa non è una sintassi diversa: sta semplicemente omettendo le parentesi opzionali e riformattando un po '.

Come menzionato Martin, in questo caso è anche possibile esprimere il requisito logico usando i join interni seguiti da un join esterno destro:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
RIGHT JOIN dbo.Autos AS A
    ON A.ModelID = M.ModelID;

Tutti e tre i moduli di query sopra utilizzano la stessa sintassi di join ANSI. Inoltre, tutti e tre producono lo stesso piano di esecuzione fisica con il set di dati fornito:

Piano di esecuzione comune

Come ho detto nella mia risposta alla tua domanda precedente, le query che esprimono esattamente lo stesso requisito logico non produrranno sempre lo stesso piano di esecuzione. Quale modulo di query logica preferisci utilizzare è in gran parte una questione di stile. Non esiste alcuna correlazione tra uno stile particolare e piani di query "migliori" in generale. In generale, sconsiglioi di riscrivere una query per ottenere un piano particolare se la nuova query non fosse autenticamente logicamente identica all'originale.

Lo standard SQL consente anche le FROMsubquery di clausole, quindi un altro modo per scrivere la stessa specifica di query è:

SELECT * 
FROM dbo.Autos AS A
LEFT JOIN
(
    SELECT
        N.ManufacturerID,
        ManufacturerName = N.Name,
        M.ModelID,
        ModelName = M.Name
    FROM dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) AS R1
    ON R1.ModelID = A.ModelID;

Usando la sintassi tradizionale, dobbiamo cambiare il join in `Produttori in un join esterno, in questo modo ... ma questo cambia il piano di query.

Ciò probabilmente modifica il significato della query, nel qual caso non è tecnicamente un'alternativa valida (ma vedi il commento di ypercube sulla tua domanda).

Le parentesi (facoltative) nella sintassi del join ANSI sono lì proprio per requisiti di join più complessi come questo, quindi non dovresti aver paura di usarli dove necessario.

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.