Sei sulla pagina 1di 41

Subquery

innestate e correlate

Basi di Dati e Lab


Subquery
Le subquery sono query che compaiono all’interno di un'istruzione SELECT, INSERT,
UPDATE o DELETE o all’interno di un'altra query.

In alcuni casi (ma non in tutti) una subquery può essere trasformata in un join
equivalente, e viceversa
Le subquery sono dette anche query interne o istruzioni SELECT interne. L'istruzione
che include una subquery è detta anche query esterna o istruzione SELECT esterna
Le query interne possono a loro volta contenere altre query interne
Sono consentiti fino a 32 livelli di nidificazione
Una subquery può essere specificata in qualsiasi posizione in cui sia consentito
inserire un'espressione, a condizione che venga restituito un solo valore
ESEMPI
Le subquery sono query che compaiono all’interno di un'istruzione SELECT, INSERT,
UPDATE o DELETE o all’interno di un'altra query.
select E.*, (select Cnome from C where C.cc=E.cc)
from E
where E.voto>=28

UPDATE S
SET Acorso=3
WHERE SNome IN (Select D.Cnome
FROM D
WHERE D.CD like 'M%')
insert into D
select S.Matr, S.Snome, S.citta
from S
where S.ACorso=2
ESEMPI
Le subquery sono query che compaiono all’interno di un'istruzione SELECT,
INSERT, UPDATE o DELETE o all’interno di un'altra query.

Select S.Snome
From S
where S.Matr IN (select Matr
from E
where E.voto=30 or E.voto=33)
Subquery annidate
Le subquery annidate (o innestate, o nidificate)
◦ consistono di una query (“esterna”) con una clausola WHERE che
include condizioni dipendenti da risultati prodotti da altre query
(“interne”)
◦ le condizioni in cui compaiono le query interne sono tipicamente
quelle di confronto con insiemi di valori
“<> ALL” equivalente a “NOT IN”;
“IN” equivalente a “= ANY”
Subquery scalari annidate
Le subquery SCALARI annidate

◦ producono un result set costituito da una sola riga e colonna,


utilizzabile ovunque sia assegnabile un valore scalare singolo

◦ permettono di rendere le query dinamiche e rispondenti ai


dati presenti nel database, piuttosto che a valori
esplicitamente impostati nel codice
DB DI TEST – CONCORSI FOTOGRAFICI
Per la creazione delle tabelle utilizzate nelle seguenti slide utilizzare gli script appositi
(vedi materiale didattico ‘DB_FOTO_SCRIPT.sql’)
REVERSE ENGINEERING – SCHEMA ER
REVERSE ENGINEERING del DB DI TEST – CONCORSI FOTOGRAFICI
Ovvero disegnare lo schema ER a partire dal quale è stato generato il
db fornito

NOTE:
◦ Non basarsi solo sulla rappresentazione grafica fornita da SQL server

◦ Controllare vincoli impostati in fase di creazione delle tabelle

◦ Partire dalle tabelle in cui non sono definiti dei legami di foreign key
(tabelle che possono essere riferite da altre tabelle), poi continuare con le
tabelle dipendenti.

◦ I valori immessi nel db posso aiutarci per la comprensione


1- TABELLE RIFERITE

CREATE TABLE [dbo].[Concorso] (


[idconcorso] [int] NOT NULL PRIMARY KEY,
[nome] [varchar] (200) NOT NULL,
[annoedizione] [int] NOT NULL,
[digitale] [bit] NOT NULL CHECK
([digitale]=0 OR [digitale]=1)
CREATE TABLE [dbo].[Fotografo] (
--1 = concorso digitale 0= concorso
[idfotografo] [int] NOT NULL PRIMARY KEY, tradizionale
)
[nome] [nvarchar] (100) NOT NULL,
GO
[nazionalita] [nvarchar] (100)
)
GO
2- TABELLE DIPENDENTI

CREATE TABLE [dbo].[Fotografia] (


[idfotografia] [int] NOT NULL PRIMARY KEY,
[tema] [nvarchar] (100) NOT NULL,
[idfotografo] [int] NOT NULL REFERENCES
[Fotografo]
)
GO
2- TABELLE DIPENDENTI

CREATE TABLE [dbo].[Valutazione] (


[idfotografia] [int] NOT NULL REFERENCES [Fotografia],
[idconcorso] [int] NOT NULL REFERENCES [Concorso],
[voto] [int] NOT NULL
PRIMARY KEY ([idfotografia],[idconcorso])
)
GO
Esempio
QUERY: Elencare le fotografie che hanno ricevuto la valutazione più
alta

Adottiamo una strategia in due passi


◦ Troviamo la valutazione più alta
◦ Cerchiamo le fotografie (idfotografia) che hanno ricevuto tale valutazione
Sviluppo dell’esempio
Primo passo – Troviamo la valutazione più alta
<valutazione_maggiore> = (select MAX(voto)
from Valutazione)

Secondo passo - Cerchiamo le fotografie (idfotografia) che hanno ricevuto tale valutazione

Select idfotografia
from Valutazione
where voto= ( <valutazione_maggiore> )

INFINE - Componiamo la query complessiva


Select idfotografia
from Valutazione
where voto= (select MAX(voto)
from Valutazione)
SOLUZIONE ALTERNATIVA
QUERY: Elencare le fotografie che hanno ricevuto la valutazione più
alta

Select idfotografia
from Valutazione
where voto >= ALL (select voto
from Valutazione)

15
Modifica esempio
QUERY: Elencare dati dei fotografi le cui fotografie hanno ricevuto la
valutazione più alta

Select F.*
from (Fotografo F join Fotografia FA on
(F.idfotografo=FA.idfotografo))
join Valutazione V on (V.idfotografia=FA.idfotografia)
where voto= (select MAX(voto)
from Valutazione)
Soluzione Alternativa
QUERY: Elencare dati dei fotografi le cui fotografie hanno ricevuto la valutazione più alta

Select *
from Fotografo
where idfotografo IN (select idfotografo
from Fotografia
where idfotografia IN (select idfotografia
from Valutazione
where voto= (select MAX(voto)

from Valutazione) ) )
Osservazioni
La SELECT innestata può essere ovunque; altro esempio –
selezionare idfotografia e voto medio di tutte le fotografie valutate
Select idfotografia, votomediototale =( select AVG(voto)
from Valutazione)
from Valutazione

Se la subquery viene introdotta da un operatore di confronto (=, < >, >, > =, <,
! >, ! <, or < =), allora deve riportare sempre un unico valore

Subquery introdotte con [NOT] IN o ALL, ANY, o [NOT] EXISTS


possono riportare delle liste di risultati
Esempio di uso del predicato NOT
IN
Selezionare i dati delle fotografie scattate da fotografi non italiani
select *
from Fotografia
WHERE idfotografo NOT IN
(
SELECT idfotografo
FROM Fotografo
WHERE nazionalita='italiano'
)

Attenzione alla presenza di valori NULL!


Questa query seleziona i fotografi la cui nazionalità non è italiana, ma cosa
succede ai fotografi che hanno nazionalità null ?!
Valori null
y  {x1,x2}  ( (y = x1) OR (y = x2) ),
quindi y {x1,x2} ( (y <> x1) AND (y <> x2) ),
in questo caso se uno degli x è NULL, allora il confronto
y<>NULL è indefinito ed il predicato è falso.

I valori NULL vanno esclusi accuratamente dal result set


usando la funzione IS NULL
Valori null
Riscrivere la query: Selezionare i dati dei fotografi che
non sono italiani (cioè di cui è noto non siano di
nazionalità italiana)
select *
from Fotografia
WHERE idfotografo NOT IN
(
SELECT idfotografo
FROM Fotografo
WHERE nazionalita='italiano'
or nazionalita is null
)
Soluzione Alternativa
select *
from Fotografia

WHERE idfotografo IN
(Select idfotografo
from Fotografo
WHERE nazionalita<>'italiano'
)

22
Alcuni esempi - Subquery come
operando scalare
Il caso più semplice di subquery è quella che restituisce un singolo valore. La si può
usare in qualsiasi punto sia possibile utilizzare un valore di colonna. L'uso più frequente
lo troviamo come operatore di confronto:
SELECT colonna1 FROM t1
WHERE colonna1 = (SELECT MAX(colonna2) FROM t2);
Questa query estrae i valori di colonna1 nella tabella t1 che sono uguali al valore
massimo di colonna2 nella tabella t2.

Select idfotografia
from Valutazione
where voto= (select MAX(voto)
from Valutazione)
Alcuni esempi - Subquery che
restituiscono colonne
Quando una subquery restituisce una colonna, può essere usata per fare
confronti attraverso gli operatori ANY, SOME, IN e ALL:
SELECT s1 FROM t1
"seleziona da t1 i valori di s1 che sono
WHERE s1 > ANY (SELECT s1 FROM t2); maggiori di almeno 1 dei valori di s1 su
t2".

Esempio: selezionare le foto la cui valutazione è maggiore di una delle


valutazioni assegnata alla foto idfotografia=1
Select idfotografia
from Valutazione
where voto >ANY (select voto
from Valutazione where idfotografia=1)

"SOME" è un costrutto T-SQL equivalente in tutto e per tutto ad


"ANY".
Alcuni esempi - Subquery che
restituiscono colonne
Quando una subquery restituisce una colonna, può essere usata per fare confronti
attraverso gli operatori ANY, SOME, IN e ALL:
SELECT s1 FROM t1
"seleziona da t1 i valori di s1 che sono
WHERE s1 IN (SELECT s1 FROM t2); uguali ad almeno 1 dei valori di s1 su
t2".

Esempio: selezionare le foto che hanno almeno una valutazione uguale ad una
valutazione assegnata alla foto idfotografia=1

Select idfotografia
from Valutazione
where voto IN (select voto
from Valutazione where idfotografia=1)

"IN" è sinonimo di "= ANY".


Alcuni esempi - Subquery che
restituiscono colonne
Quando una subquery restituisce una colonna, può essere usata per fare
confronti attraverso gli operatori ANY, SOME, IN e ALL:
SELECT s1 FROM t1 "seleziona da t1 i valori di s1 che sono
diversi da tutti i valori di s1 su t2".
WHERE s1 <> ALL (SELECT s1 FROM t2);
Esempio: selezionare le foto che hanno ricevuto almeno una valutazione
diversa dalle valutazioni assegnata alla foto idfotografia=1

Select idfotografia
from Valutazione
where voto <> ALL (select voto
from Valutazione where idfotografia=1)

"NOT IN" è sinonimo di "<> ALL".


Alcuni esempi - Subquery che
restituiscono righe
Quando una subquery restituisce una singola riga, può essere usata per fare
confronti attraverso i costruttori di righe:
SELECT colonna1,colonna2 FROM t1
WHERE (colonna1,colonna2) IN
(SELECT colonna1,colonna2 FROM t2);
Questa query estrae le righe di t1 in cui i valori di colonna1 e colonna2 sono
ripetuti in una riga di t2.

NON IMPLEMENTATO in SQL SERVER!!!


Riduzione di query annidate
Alcune query annidate introdotte dagli operatori IN, ANY (qualsiasi operatore
di confronto), EXISTS (con subquery correlata) possono essere trasformate in
query semplici equivalenti con join

Le query annidate formulate con NOT IN, ALL, NOT EXISTS (con subquery
correlata) non possono essere trasformate in query semplici con join
Esempio
Selezionare le foto che sono state valutate in uno dei concorsi in
cui è stata valutata la foto idfotografia=1

Select idfotografia
from Valutazione
where idconcorso IN (select idconcorso
from Valutazione where idfotografia=1)
è uguale a
Select V1.idfotografia
from Valutazione V1, Valutazione V2
where V1.idconcorso=V2.idconcorso
and V2.idfotografia=1
Subquery correlate

Le subquery CORRELATE sono subquery in cui il valore della SELECT interna è legato a
valori della query esterna

SELECT * FROM t1
WHERE colonna1 = ANY
(SELECT colonna1
FROM t2 WHERE t2.colonna2 = t1.colonna2);

In questa subquery, la clausola WHERE contiene un riferimento alla tabella t1, che
tuttavia non è nominata nella clausola FROM della subquery stessa: la troviamo infatti
nella FROM della query esterna.
Query di questo tipo richiedono che la subquery venga rieseguita ad ogni riga estratta
dalla query esterna, e di conseguenza non sono molto performanti. Meglio quindi evitarle
quando possibile: in alcuni casi una subquery correlata è trasformabile in un join.
Esempio -Subquery correlate
Le subquery CORRELATE sono subquery in cui il valore della SELECT interna è
legato a valori della SELECT esterna

Esempio: selezionare per ogni concorso la valutazione della fotografia vincente,


ovvero la fotografia che ha ottenuto il voto più alto all’interno del concorso
Select *
from Concorso C join Valutazione V on
(C.idconcorso=V.idconcorso)
where V.voto = (select MAX(voto)
from Valutazione V2
where V2.idconcorso=V.idconcorso)
Soluzione alternativa
Esempio: selezionare per ogni concorso la valutazione della fotografia
vincente, ovvero la fotografia che ha ottenuto il voto più alto
Un’altra possibile soluzione era utilizzare il raggruppamento

Select V.idconcorso, max(voto) as valutazione_massima


from Concorso C join Valutazione V on
(C.idconcorso=V.idconcorso)
group by V.idconcorso
Soluzione alternativa
Se voglio selezionare anche idfotografia della fotografia vincente per
ciascun concorso, la query si complica
Select C.idconcorso, max(voto) as valutazione_massima,
idfotografia =(select idfotografia
from Valutazione V1
where V1.idconcorso=C.idconcorso
and V1.voto=(select max(voto)
from Valutazione V2
where C.idconcorso=V2.idconcorso))
from Concorso C join Valutazione V on
(C.idconcorso=V.idconcorso)
group by C.idconcorso
Esempio -Subquery correlate
Esempio: selezionare per ogni fotografia il voto medio ottenuto dalla stessa nei diversi concorsi
Select idfotografia,
votomedioricevuto =( select AVG(voto)
from Valutazione V2
where V2.idfotografia=V1.idfotografia)
from Fotografia V1
Nota (siccome voto è un intero per ottenere un valor medio decimale occorre convertirlo a un
valore decimale, ad es. real)
Select idfotografia,
votomedioricevuto =( select avg(convert(real, voto))
from Valutazione V2
where V2.idfotografia=V1.idfotografia)
from Fotografia V1
Soluzione alternativa
Esempio: selezionare per ogni fotografia il voto medio ottenuto dalla stessa nei diversi
concorsi
Un’altra possibile soluzione era utilizzare il raggruppamento

Select v1.idfotografia, avg(convert(real, voto))


from Valutazione V1
group by idfotografia
Esempio - Subquery correlate
nella lista select
Esempio: selezionare per ogni fotografia il voto medio ottenuto dalla stessa nei diversi
concorsi e il numero di concorsi a cui ha partecipato
SELECT idfotografia,
voto_medio= (select avg(convert (real,voto)) from
valutazione V2
where V2.idfotografia=F.idfotografia),
numero_concorsi=
(select count(*) from Valutazione V1
where V1.idfotografia=F.idfotografia)
from Fotografia f
Esempio - Subquery correlate
nella lista select
Esempio: selezionare per ogni fotografia il voto medio ottenuto dalla stessa nei diversi
concorsi e il numero di concorsi a cui ha partecipato

Con il group by

select F.idfotografia, avg(convert (real,voto)),


count(idconcorso)
from Fotografia F LEFT JOIN Valutazione V ON
(F.idfotografia=V.idfotografia)
group by F.idfotografia
Subquery correlate - Exists
Le subquery correlate vengono usate a volte con le clausole EXISTS e NOT
EXISTS; la clausola EXISTS è vera quando la subquery restituisce almeno una
riga, mentre è falsa nel caso opposto. Ovviamente NOT EXISTS funziona al
contrario.

SELECT DISTINCT tipoNegozio FROM negozi


WHERE EXISTS (SELECT * FROM negozi_citta
WHERE negozi_citta.tipoNegozio = negozi.tipoNegozio);

Ipotizzando che la tabella negozi_citta contenga i tipi di negozio presenti nelle


varie città, questa query estrae i tipi di negozio che sono presenti in almeno
una città.
Selezionare i dati dei fotografi che non hanno scattato alcuna
fotografia
Soluzione con il NOT EXISTS
select *
from Fotografo F
where not exists (select *
from Fotografia
where idfotografo=F.idfotografo)

Soluzione con il NOT IN


select *
from Fotografo F
where idfotografo not in (select idfotografo
from Fotografia )
Esercizi sul db concorsi fotografici
1. Riportare i dati delle fotografie valutate al concorso
'National Wildlife Photo Contest' del 2008.

2. Selezionare per ciascun concorso il numero di fotografie


valutate

3. Riportare i dati delle fotografie che non sono mai state


valutate in un concorso

4. Per ogni fotografo riportare il primo concorso a cui ha


partecipato (in termini di anno edizione del concorso e a
parità in base al nome del concorso)
Esercizi più complessi
5. (DIVISIONE) Selezionare i dati dei fotografi che hanno
prodotto foto per ciascun tema prodotto da ‘Helmut Newton’

6. (CON GROUP BY) Selezionare, per ogni edizione di un


concorso, il fotografo vincitore (cioè colui che ha ottenuto la
valutazione più alta per la foto presentata al concorso).

7. (CON GROUP BY) Selezionare per i concorsi che hanno


valutato lo stesso numero di fotografie valutate al concorso
'National Wildlife Photo Contest' del 2008