Interrogazioni nidificate

11 / 100

Interrogazioni nidificate

Una interrogazione nidificata è una interrogazione che contiene un’altra interrogazione. SQL non pone limiti al livello di annidamento ma solitamente più di due annidamenti rendono incomprensibile una query ad un umano (una macchina, invece, non ha problemi di comprensione in questo caso).

E’ possibile nidificare una interrogazione nella clausola where (vedremo un’altra forma di annidamento parlando delle viste).

In particolare una espressione può essere confrontata mediante gli usuali operatori di confronto con una interrogazione.

L’operatore di confronto è seguito dalla parola chiave any oppure all.

Nel primo caso, il predicato è vero se ha successo il confronto (secondo l’operatore usato) tra il valore dell’espressione e almeno uno tra i valori risultato della query. Nel secondo caso, il predicato è vero se ha successo il confronto (secondo l’operatore usato) tra il valore dell’espressione e tutti i valori risultato della query. L’espressione e il risultato dell’interrogazione devono essere compatibili, cioè avere lo stesso numero di attributi e gli attributi devono evere domini compatibili.

Vediamo alcuni esempi. Consideriamo le tabelle attore a autore usate nelle interrogazioni insiemistiche. La seguente interrogazione seleziona tutti gli attori che sono anche autori. Si noti che abbiamo già scritto la stessa query usando l’operatore intersect:

select nome
from attore
where nome = any (select nome
                  from autore)

Si noti che la query interna è scritta tra parentesi tonde. La seguente interrogazione seleziona tutti gli attori che non sono autori. Si noti che abbiamo già scritto la stessa query usando l’operatore except:

select nome
from attore
where nome <> all (select nome
                   from autore)

Selezionare tutti gli attori o autori è invece possibile solo usando l’operatore union. Le combinazioni = any e <> all sono molto frequenti e hanno un nome: nel primo caso posso usare la parola chiave in e nel secondo not in. Ad esempio:

select nome
from attore
where nome not in (select nome
                   from autore)

Se il nome e il cognome di un attore fosse stato specificanto usando due attributi invece che uno soltato, avremmo potuto usare il costruttore di tupla (nome, cognome) in questo modo:

select nome, cognome
from attore
where (nome, cognome) not in (select nome, cognome
                              from autore)

Il nome del dipendente con lo stipendio massimo può essere estratto nei seguenti due modi:

select nome, stipendio
from dipendente
where stipendio >= all (select stipendio
                        from dipendente)
select nome, stipendio
from dipendente
where stipendio = (select max(stipendio)
                   from dipendente)

Si noti che nel secondo caso la parola chiave all o any può essere omessa in quanto la query interna restituisce un solo elemento.

L’interrogazione interna può essere sostituita con un insieme esplicito. L’interrogazione seguente recupera tutti i nomi dei dipendenti in un certo insieme esplicito:

select nome
from dipendente
where nome in ("Elisa Bottega", "Lorenzo Vignando", "Barbara Altissimo")

Le interrogazioni nidificate viste fin ora possono essere risolte indipendentemente dall’interrogazione che le contiene. Ad esempio, nell’ultima interrogazione scritta, è possibile prima di tutto risolvere la query interna, cioè calcolare il massimo stipendio, e poi elaborare la query esterna confrontando il massimo stipendio con il valore dello stipendio di ogni dipendente. Questa soluzione è più efficiente rispetto a quella che valuta la query interna per ogni tupla della query esterna. Questa soluzione non può però sempre essere seguita. Consideriamo una interrogazione che restituisce gli attori per i quali esiste un altro attore con la medesima data di nascita. Osserviamo innanzitutto che è possibile risolvere l’interrogazione con il seguente join:

select distinct A1.nome
from attore A1 join attore A2 on 
(A1.dataDiNascita = A2.dataDiNascita) and
(A1.nome <> A2.nome)

Vediamo ora una soluzione che usa una query nidificata e l’operatore exists. Questo operatore ha come argomento una query interna e restituisce vero se e soltanto se il risultato della query argomento contiene qualche elemento:

select A1.nome
from attore A1
where exists (select A2.nome 
              from attore A2
              where (A1.dataDiNascita = A2.dataDiNascita) and
                    (A1.nome <> A2.nome))

Questo tipo di query si dicono query nidificate con passaggio di variabili. In particolare, la variabile A1, creata nella query esterna per la prima istanza della tabella attore, viene passata alla query interna che ne fa uso. In tal caso la query interna non può essere valutata indipendentemente da quella esterna in quanto si serve di variabili definite a livello di query esterna. Dunque l’unico modo di risolvere questa query consiste nel valutare la query interna per ogni tupla della query esterna. La visibilità delle variabili in SQL segue la seguente semplice regola: una variabile è visibile nella query che l’ha definita o in una query nidificata in essa (ad un qualsiasi livello).

Vediamo un esempio che usa la negazione dell’operatore exists: gli attori per i quali non esiste un altro attore con la medesima data di nascita:

select A1.nome
from attore A1
where not exists (select A2.nome 
                  from attore A2
                  where (A1.dataDiNascita = A2.dataDiNascita) and
                        (A1.nome <> A2.nome))

Una formulazione che non usa le query nidificate è la seguente:

select nome
from attore 
  except 
select A1.nome
from attore A1 join attore A2 on 
(A1.dataDiNascita = A2.dataDiNascita) and
(A1.nome <> A2.nome)
Translate »