Basi di Dati: Corso di laboratorio Lezioni 6 7 Raffaella Gentilini 1 / 46
Sommario 1 Subquery (o Interrogazioni Nidificate) Interrogazioni Annidate con Predicati di Confronto Interrogazioni Annidate con Predicati ALL, ANY Interrogazioni Annidate con Predicato EXISTS Subquery Correlate Livelli Multipli di Annidamento Subquery e Divisione 2 / 46
Subquery Subquery e uno statement in un altro statement si puo trovare nei seguenti punti dei seguenti statement: 1 nelle clausole WHERE e HAVING dello statement SELECT; 2 nella clausola WHERE di un INSERT INTO, DELETE FROM, UPDATE; 3 nella clausola SET di un comando UPDATE. dalla possibilita di annidare query deriva l aggettivo structured di SQL (Structured Query Language) 3 / 46
Subquery Subquery La forma nidificata e piu procedurale della forma flat, e per questo talvolta piu leggibile Una subquery e sempre tra parentesi e puo restituire: un singolo valore che si puo usare come espressione scalare una colonna che si puo usare come elenco di costanti una tabella che si puo usare ovunque e possibile usare una tabella Le subquery non possono contenere operatori insiemistici 4 / 46
Subquery Si consideri lo schema relazionale visto nelle lezioni precedente: persona(id persona, codice fiscale, nome, cognome, data nascita) corso(id corso, id insegnante, sigla, crediti, descrizione) frequenza(id studente,id corso,voto): dove id studente ed id corso sono chiavi esterne su persona e corso SELECT persona.nome, persona.cognome FROM persona WHERE persona.id persona IN (SELECT corso.id insegnante FROM corso) 5 / 46
Subquery Si consideri lo schema relazionale visto nelle lezioni precedente: persona(id persona, codice fiscale, nome, cognome, data nascita) corso(id corso, id insegnante, sigla, crediti, descrizione) frequenza(id studente,id corso,voto): dove id studente ed id corso sono chiavi esterne su persona e corso SELECT persona.nome, persona.cognome FROM persona WHERE persona.id persona IN (SELECT corso.id insegnante FROM corso) Nome e cognome degli insegnanti che tengono almeno un corso 6 / 46
Subquery e operatore IN Il predicato IN Il predicato puo essere applicato a liste di attributi puo essere negato SELECT persona.nome, persona.cognome FROM persona WHERE persona.id persona NOT IN (SELECT corso.id insegnante FROM corso WHERE corso.id insegnante IS NOT NULL) 7 / 46
Subquery e operatore IN Il predicato IN Il predicato puo essere applicato a liste di attributi puo essere negato SELECT persona.nome, persona.cognome FROM persona WHERE persona.id persona NOT IN (SELECT corso.id insegnante FROM corso WHERE corso.id insegnante IS NOT NULL) Nome e cognome degli insegnanti che non tengono alcun corso 8 / 46
Subquery e Predicati di Confronto Gli operatori di confronto =, <>, <, >, <=, >= si possono usare solo se l interrogazione annidata al piu una riga (subquery scalare) se la subquery non restituisce alcuna riga, il confronto da come risultato NULL in PostgreSQL, solo <> ed = sono supportati se il confronto coinvolge piu colonne SELECT id corso FROM corso WHERE crediti = (SELECT MAX(crediti) FROM corso ) 9 / 46
Subquery e Predicati di Confronto Gli operatori di confronto =, <>, <, >, <=, >= si possono usare solo se l interrogazione annidata al piu una riga (subquery scalare) se la subquery non restituisce alcuna riga, il confronto da come risultato NULL in PostgreSQL, solo <> ed = sono supportati se il confronto coinvolge piu colonne SELECT id corso FROM corso WHERE crediti = (SELECT MAX(crediti) FROM corso ) Id dei corsi con il massimo numero di crediti 10 / 46
Subquery e Predicati di Confronto Gli operatori di confronto =, <>, <, >, <=, >= si possono usare solo se l interrogazione annidata al piu una riga (subquery scalare) se la subquery non restituisce alcuna riga, il confronto da come risultato NULL in PostgreSQL, solo <> ed = sono supportati se il confronto coinvolge piu colonne SELECT id corso FROM corso WHERE crediti = (SELECT MAX(crediti) FROM corso ) Id dei corsi con il massimo numero di crediti 11 / 46
Subquery e Predicato ANY Per usare predicati di confronto con selezioni che possono restituire piu di una riga, occorre usare le quantificazioni ALL oppure ANY Il predicato ANY da vero se il confronto e vero per almeno una riga, da falso se tutti i confronti di riga sono falsi, o la subquery restituisce una tabella vuota se non ci sono confronti veri ed almeno uno e NULL, allora il risultato e NULL Il predicato IN e equivalente a = ANY 12 / 46
Subquery e Predicato ANY SELECT persona.nome, persona.cognome FROM persona WHERE persona.id persona = ANY (SELECT corso.id insegnante FROM corso) 13 / 46
Subquery e Predicato ANY SELECT persona.nome, persona.cognome FROM persona WHERE persona.id persona = ANY (SELECT corso.id insegnante FROM corso) Nome e cognome degli insegnanti che tengono almeno un corso 14 / 46
Subquery e Predicato ALL Il predicato ALL da vero se tutti i confronti di riga sono veri o la subquery restituisce la tabella vuota, da falso se almeno un confronto e falso restituisce NULL se non ci sono confronti falsi, ma almeno uno e indefinito Il predicato NOT IN e equivalente a <> ALL 15 / 46
Subquery e Predicato ALL SELECT persona.nome, persona.cognome FROM persona WHERE persona.id persona <> ALL (SELECT corso.id insegnante FROM corso WHERE corso.id insegnante IS NOT NULL) 16 / 46
Subquery e Predicato ALL SELECT persona.nome, persona.cognome FROM persona WHERE persona.id persona <> ALL (SELECT corso.id insegnante FROM corso WHERE corso.id insegnante IS NOT NULL) Nome e cognome degli insegnanti che non tengono alcun corso 17 / 46
Subquery e Predicato ALL SELECT persona.nome, persona.cognome FROM persona WHERE persona.id persona <> ALL (SELECT corso.id insegnante FROM corso WHERE corso.id insegnante IS NOT NULL) Nome e cognome degli insegnanti che non tengono alcun corso I valori NULL nella colonna id insegnante devono essere soppressi per evitare che rendano il predicato <> ALL non vero! 18 / 46
Subquery e Predicato EXISTS Il predicato EXISTS e vero se la subquery restituisce almeno una riga altrimenti e falso Facendo uso di NOT EXISTS e possibile verificare se la subquery non restituisce alcuna tupla SELECT persona.nome, persona.cognome FROM persona WHERE EXISTS (SELECT FROM corso WHERE crediti > 3) 19 / 46
Subquery e Predicato EXISTS Il predicato EXISTS e vero se la subquery restituisce almeno una riga altrimenti e falso Facendo uso di NOT EXISTS e possibile verificare se la subquery non restituisce alcuna tupla SELECT persona.nome, persona.cognome FROM persona WHERE EXISTS (SELECT FROM corso WHERE crediti > 3) Si noti che la query sopra non e molto interessante, in quanto il risultato della subquery e sempre lo stesso, ovvero non dipende dalla specifica tupla del blocco esterno 20 / 46
Subquery Correlate Se la subquery fa riferimento al blocco esterno, allora si dice correlata SELECT persona.nome, persona.cognome FROM persona P WHERE EXISTS (SELECT FROM corso C WHERE crediti > 3 AND id insegnante = P.id persona ) Adesso il risultato della query innestata dipende dalla persona specificata, e la semantica diventa: Per ogni tupla del blocco esterno, considera il valore di P.id persona e risolvi la query innestata 21 / 46
Subquery Correlate Se la subquery fa riferimento al blocco esterno, allora si dice correlata SELECT persona.nome, persona.cognome FROM persona P WHERE EXISTS (SELECT FROM corso C WHERE crediti > 3 AND id insegnante = P.id persona ) Insegnanti che tengono almeno un corso con piu di 3 crediti Adesso il risultato della query innestata dipende dalla persona specificata, e la semantica diventa: Per ogni tupla del blocco esterno, considera il valore di P.id persona e risolvi la query innestata 22 / 46
Subquery Correlate SELECT persona.nome, persona.cognome FROM persona WHERE EXISTS ( SELECT frequenza.voto FROM frequenza WHERE persona.id persona=frequenza.id studente AND frequenza.voto IS NOT NULL) AND 10 <= ALL ( SELECT frequenza.voto FROM frequenza WHERE persona.id persona=frequenza.id studente AND frequenza.voto IS NOT NULL) 23 / 46
Subquery Correlate SELECT persona.nome, persona.cognome FROM persona WHERE EXISTS ( SELECT frequenza.voto FROM frequenza WHERE persona.id persona=frequenza.id studente AND frequenza.voto IS NOT NULL) AND 10 <= ALL ( SELECT frequenza.voto FROM frequenza WHERE persona.id persona=frequenza.id studente AND frequenza.voto IS NOT NULL) Studenti che hanno preso solo voti superiori a 10 24 / 46
Livelli Multipli di Annidamento Una subquery puo fare uso a sua volta di altre subquery. Il risultato si puo ottenere risolvendo a partire dal blocco piu interno. persone(nome, eta, reddito) maternita (madre,figlio) paternita (padre,figlio) SELECT nome,reddito FROM persone WHERE nome IN. (SELECT padre FROM paternita. WHERE figlio =ANY. (SELECT nome FROM persone. WHERE reddito>40)) 25 / 46
Unnesting Unnesting E spesso possibile ricondursi ad una forma flat, ma la cosa non e sempre cosi ovvia. Ad esempio, la query precedente si puo anche scrivere come: SELECT DISTINCT p.nome,p.reddito FROM persone p, paternita, persone f WHERE p.nome=padre AND figlio=f.nome AND f.reddito>40 26 / 46
Subquery e Divisione Impiegati(CodiceImp, Nome, Sede, Ruolo, Stipendio) Sedi(Sede, Responsabile, Citta ) Le subquery permettono di eseguire la divisione relazionale. Sedi in cui sono presenti tutti i ruoli equivale a Quali sono le sedi che verificano: Per ogni ruolo, tale ruolo e presente nella sede? In SQL non esiste l operatore di quantificazione universale 27 / 46
Subquery e Divisione Impiegati(CodiceImp, Nome, Sede, Ruolo, Stipendio) Sedi(Sede, Responsabile, Citta ) Le subquery permettono di eseguire la divisione relazionale. Sedi in cui sono presenti tutti i ruoli equivale a Quali sono le sedi che verificano: Per ogni ruolo, tale ruolo e presente nella sede? Tuttavia xp(x) P(x). 28 / 46
Subquery e Divisione Impiegati(CodiceImp, Nome, Sede, Ruolo, Stipendio) Sedi(Sede, Responsabile, Citta ) Le subquery permettono di eseguire la divisione relazionale. Sedi in cui sono presenti tutti i ruoli equivale a Quali sono le sedi che verificano: Per ogni ruolo, tale ruolo e presente nella sede? Dunque possiamo scrivere in SQL una query equivalente a quelle sopra, riformulando il problema come: 29 / 46
Subquery e Divisione Impiegati(CodiceImp, Nome, Sede, Ruolo, Stipendio) Sedi(Sede, Responsabile, Citta ) Le subquery permettono di eseguire la divisione relazionale. Sedi in cui sono presenti tutti i ruoli equivale a Quali sono le sedi che verificano: Per ogni ruolo, tale ruolo e presente nella sede? Quali sono le sedi che verificano: Non esiste un ruolo non presente nella sede? 30 / 46
Subquery e Divisione Sedi in cui non esiste un ruolo sono non presente Mediante l uso di subquery, tale interrogazione si puo formulare in SQL come: SELECT Sede FROM Sedi AS S WHERE NOT EXISTS (SELECT FROM Impiegati I1 WHERE NOT EXISTS (SELECT FROM Impiegati I2 WHERE S.Sede=I2.Sede AND. I1.Ruolo=I2.Ruolo)) 31 / 46
Subquery e Divisione SELECT Sede FROM Sedi AS S WHERE NOT EXISTS (SELECT FROM Impiegati I1 WHERE NOT EXISTS (SELECT FROM Impiegati I2 WHERE S.Sede=I2.Sede AND. I1.Ruolo=I2.Ruolo)) Il blocco piu interno viene valutato per ogni combinazione di S ed I1 32 / 46
Subquery e Divisione SELECT Sede FROM Sedi AS S WHERE NOT EXISTS (SELECT FROM Impiegati I1 WHERE NOT EXISTS (SELECT FROM Impiegati I2 WHERE S.Sede=I2.Sede AND. I1.Ruolo=I2.Ruolo)) Il blocco intermedio funge da divisore (riguarda I1.Ruolo) 33 / 46
Subquery e Divisione SELECT Sede FROM Sedi AS S WHERE NOT EXISTS (SELECT FROM Impiegati I1 WHERE NOT EXISTS (SELECT FROM Impiegati I2 WHERE S.Sede=I2.Sede AND. I1.Ruolo=I2.Ruolo)) Data una sede S, se in S manca un ruolo: 34 / 46
Subquery e Divisione SELECT Sede FROM Sedi AS S WHERE NOT EXISTS (SELECT FROM Impiegati I1 WHERE NOT EXISTS (SELECT FROM Impiegati I2 WHERE S.Sede=I2.Sede AND. I1.Ruolo=I2.Ruolo)) Data una sede S, se in S manca un ruolo: la subquery piu interna non restituisce nulla 35 / 46
Subquery e Divisione SELECT Sede FROM Sedi AS S WHERE NOT EXISTS (SELECT FROM Impiegati I1 WHERE NOT EXISTS (SELECT FROM Impiegati I2 WHERE S.Sede=I2.Sede AND. I1.Ruolo=I2.Ruolo)) Data una sede S, se in S manca un ruolo: la subquery piu interna non restituisce nulla quindi la subquery intermedia restituisce almeno una tupla 36 / 46
Subquery e Divisione SELECT Sede FROM Sedi AS S WHERE NOT EXISTS (SELECT FROM Impiegati I1 WHERE NOT EXISTS (SELECT FROM Impiegati I2 WHERE S.Sede=I2.Sede AND. I1.Ruolo=I2.Ruolo)) Data una sede S, se in S manca un ruolo: la subquery piu interna non restituisce nulla quindi la subquery intermedia restituisce almeno una tupla... e la clausola WHERE non e soddisfatta per S 37 / 46
Subquery: come eseguire la divisione relazionale Vediamo un altro esempio: persona(id persona, codice fiscale, nome, cognome, data nascita) corso(id corso, id insegnante, sigla, crediti, descrizione) frequenza(id studente,id corso,voto) Chi sono gli studenti che seguono tutti i corsi di matematica? equivale a Chi sono gli studenti che verificano: Per ogni corso di matematica, lo studente segue tale corso?... e possiamo scrivere in SQL una query equivalente come: 38 / 46
Subquery: come eseguire la divisione relazionale Vediamo un altro esempio: persona(id persona, codice fiscale, nome, cognome, data nascita) corso(id corso, id insegnante, sigla, crediti, descrizione) frequenza(id studente,id corso,voto) Chi sono gli studenti che seguono tutti i corsi di matematica? equivale a Chi sono gli studenti che verificano: Per ogni corso di matematica, lo studente segue tale corso?... e possiamo scrivere in SQL una query equivalente come: Chi sono gli studenti che verificano: Non esiste un corso di matematica non seguito dallo studente 39 / 46
Subquery: come eseguire la divisione relazionale (II) Mediante l uso di subquery, tale interrogazione si puo formulare in SQL come: SELECT DISTINCT nome,cognome FROM persona AS p WHERE NOT EXISTS ( SELECT FROM ( SELECT FROM corso WHERE sigla LIKE mat% ) AS c WHERE NOT EXISTS ( SELECT FROM persona JOIN frequenza ON id persona=id studente WHERE id persona=p.id persona AND id corso=c.corso)) 40 / 46
SQL e Divisione Un altra possibilita di riformulare il problema per tradurlo in SQL e data da: Chi sono gli studenti che verificano: Il numero di corsi di matematica seguiti dallo studente equivale al numero totale dei corsi di matematica. 41 / 46
SQL e Divisione Chi sono gli studenti che verificano: Il numero di corsi di matematica seguiti dallo studente equivale al numero totale dei corsi di matematica. SELECT id persona, nome, cognome FROM persona JOIN frequenza ON id persona=id studente JOIN corso USING (id corso) WHERE corso.sigla LIKE mat% GROUP BY id persona,nome,cognome HAVING COUNT ( DISTINCT corso.id corso) = (SELECT COUNT( ) FROM corso WHERE sigla LIKE mat% ); 42 / 46
SQL e Divisione Chi sono gli studenti che verificano: Il numero di corsi di matematica seguiti dallo studente equivale al numero totale dei corsi di matematica.... oppure... SELECT id persona, nome, cognome FROM persona WHERE ( SELECT COUNT ( DISTINCT id corso) FROM frequenza NATURAL JOIN corso WHERE sigla LIKE mat% AND id studente=id persona) = (SELECT COUNT( ) FROM corso WHERE sigla LIKE mat% ); 43 / 46
Subquery: Aggiornamento dei Dati Le subquery si possono efficacemente usare per aggiornare i dati di una tabella sulla base di criteri che dipendono dal contenuto di altre tabelle DELETE FROM persona WHERE persona.id persona NOT IN (SELECT corso.id insegnante FROM corso WHERE corso.id insegnante IS NOT NULL) 44 / 46
Subquery: Aggiornamento dei Dati Le subquery si possono efficacemente usare per aggiornare i dati di una tabella sulla base di criteri che dipendono dal contenuto di altre tabelle DELETE FROM persona WHERE persona.id persona NOT IN (SELECT corso.id insegnante FROM corso WHERE corso.id insegnante IS NOT NULL) Elimina gli insegnanti che non tengono alcun corso 45 / 46
Subquerye CHECK Lo standard SQL prevede l utilizzo del delle interrogazioni annidate anche nella clausola CHECK Nota: Questa caratteristica non e supportata attualmente da PostgreSQL (per far fronte a cio si usano i trigger, cfr. Lezione 9).... nella creazione della tabella ImpiegatiPG, degli impiegati di Perugia CHECK ( NOT EXISTS (SELECT FROM ImpiegatiUD WHERE ImpiegatiUD.codiceImp=ImpiegatiUD.codicePG)) 46 / 46