LEZIONE14 SQL ANNIDAMENTI PAG. 1 / 5 PROF. ANDREA ZOCCHEDDU LEZIONE14 SQL ANNIDAMENTI CONCETTO DI ANNIDAMENTO LINGUAGGIO SQL QUERY ANNIDATE Per annidamento si intende la possibilità che, all interno di un comando, vi sia posto un ulteriore comando analogo. Per chi avesse studiato la programmazione imperativa, un esempio di annidamento è costituito da un ciclo while dentro il quale è posto un ulteriore ciclo while In SQL è possibile scrivere una SELECT dentro un altra SELECT, purché nei punti previsti dalla sintassi. In teoria è possibile porre delle QUERY nelle tre clausole SELECT, FROM e WHERE. In effetti non tutti gli ambienti SQL ammettono di inerirle nelle due prime clausole e quindi eviteremo di trattarle DB DI RIFERIMENTO Per procedere con le query supponiamo di avere la base di dati implementata con uno strumento adatto (es. Access, mysql, Oracle, ecc ). Per esempio è possibile ipotizzare uno schema relazionale come il seguente: GIOCATORI (IDG, NickName, Età, Sesso); PK IDG; AK NickName NOT NULL NickName, Età ; GIOCHI (IDG, Marca, Nome, EtàMinima); PK IDG; AK Nome NOT NULL Marca, Nome, EtàMinima ; GARE (IDG, Data, Player, Game, Livello, Punti); PK IDG; NOT NULL Data, Player, Game ; ANNIDAMENTO NELLA SELECT (SCONSIGLIATO) L'annidamento prevede che oltre a mostrare campi o funzioni nella clausola SELECT si possa anche inserire una query che calcola dati. La query interna deve restituire una sola colonna ed una sola riga (un solo valore) ogni volta che viene eseguita (per esempio con una funzione di aggregazione).,( SELECT COUNT(*) AS G2 WHERE G1.ID = G2.Game) FROM Giochi AS G1; Per ogni gioco (ogni riga della tabella giochi) si esegue la query interna che calcola quanti sono le gare svolte per quello specifico gioco). In pratica questa query mostra quante partite siano state svolte per ciascun gioco.
LEZIONE14 SQL ANNIDAMENTI PAG. 2 / 5 PROF. ANDREA ZOCCHEDDU ANNIDAMENTO NELLA FROM (SCONSIGLIATO) L'annidamento suppone di poter eseguire una query a partire da una tabella dinamica invece che da una struttura. La query interna deve essere calcolata prima di eseguire quella esterna. SELECT MAX (Partite) FROM ( SELECT Game, COUNT(*) AS Partite GROUP BY Game) AS G1; Prima è eseguita la query interna che restituisce il numero di partite svolte per ogni gioco. Questa tabella ha due colonne (Game e Partite). Su questa query si esegue quella esterna che calcola quale sia il valore massimo delle Partite. In sintesi restituisce il numero di partite del gioco più giocato. FROM ( SELECT MAX(Livello)AS Best ) AS G1 INNER JOIN (Gare) ON (Gare.Livello = G1.Best) ; Prima è eseguita la query interna che restituisce il massimo livello raggiunto tra tutte le partite. Questa tabella dinamica (con una sola colonna) è congiunta alla tabella Gare in base al suo campo Livello. In sintesi si trovano le partite (tutti i campi) in cui è stato raggiunto il massimo livello. ANNIDAMENTO NELLA CLAUSOLA WHERE (AMMESSO) I precedenti tipi di annidamento non sono sempre ammessi dai database esistenti. Invece in generale sono ammesse le query annidate nella clausola WHERE. Nella clausola WHERE è possibile usare operatori di confronto, l operatore IN e l operatore EXISTS. Prima di analizzare questo tipo di query è opportuno fare una considerazione sulle query nidificate. Quando una query è interna ad un altra sono possibili due diverse eventualità: - che la query interna sia indipendente da quella esterna: in questo caso la query interna può essere eseguita per prima mentre quella esterna si elabora in seguito sui risultati della prima. In questo caso si parla di binding interno. - che la query interna sia legata a quella esterna e quindi non sia possibile eseguirla per prima. In questo caso per ogni elemento (riga) della query esterna si analizza il comportamento di quella interna e si valuta il risultato. In questo caso si parla di binding esterno. ANNIDAMENTO E OPERATORI DI CONFRONTO Un primo modo di usare le query nidificate è di confrontare il risultato della query interna con un operatore di confronto (=, <>, >, <, >=, <=) rispetto ad un altro valore. Per queste query si deve fare attenzione se quelle interne sono indipendenti da quelle esterne. Vediamo alcuni esempi.
LEZIONE14 SQL ANNIDAMENTI PAG. 3 / 5 PROF. ANDREA ZOCCHEDDU =, <>, >, <, >=, <= WHERE Età = ( SELECT MAX(Età) ); La query interna è indipendente da quella esterna e viene eseguita per prima. La query interna restituisce la massima età tra i giocatori. A questo punto è possibile eseguire la query esterna che confronta l età di ciascun giocatore con il valore ottenuto in precedenza. Se coincidono la query esterna lo restituisce. In sintesi si mostrano tutti i giocatori con la più elevata età. WHERE 20 < ( SELECT COUNT(*) WHERE Gare.Player = Giocatori.ID) ; La query interna è dipendente da quella esterna e non può essere eseguita per prima. Si esegue allora la query esterna e per ciascun giocatore si esegue quella interna. La query nidificata verifica il numero di gare svolte per ciascun giocatore. La query esterna verifica se questo numero è superiore a 20. In sintesi si mostrano tutti i giocatori che hanno compiuto più di 20 partite. IN (E NOT IN) L operatore IN verifica se un dato valore è presente in un elenco. Se è presente rende Vero altrimenti rende Falso. WHERE ID IN ( SELECT Player ); La query interna è indipendente da quella esterna e viene eseguita per prima. La query interna restituisce gli identificativi dei giocatori che hanno compiuto delle gare. A questo punto è possibile eseguire la query esterna che cerca per ciascun giocatore se il proprio ID è presente nell elenco. Se lo trova allora la query esterna lo restituisce. In sintesi si mostrano tutti i giocatori che hanno svolto gare. WHERE ID NOT IN ( SELECT Player ); La query mostra tutti i giocatori che NON hanno svolto gare.
LEZIONE14 SQL ANNIDAMENTI PAG. 4 / 5 PROF. ANDREA ZOCCHEDDU OPERATORE EXISTS L operatore EXISTS verifica se una query interna è vuota oppure restituisca dei dati. Se ci sono dei dati allora rende Vero altrimenti (è vuota) rende Falso. WHERE EXISTS ( ); La query interna è indipendente da quella esterna e viene eseguita per prima. Poniamo che la query interna restituisca qualcosa (non ci interessa cosa ). A questo punto è possibile eseguire la query esterna che poiché trova sempre Vero nella clausola WHERE rende tutti i giocatori, senza alcun criterio. Questa query è inutile, o almeno lo è l annidamento. WHERE EXISTS ( WHERE Giocatori.ID = Gare.Player); Occorre eseguire la query esterna che per ogni giocatore prova ad eseguire quella interna che cerca le gare svolte dal giocatore. Se ce ne sono allora la query interna è non vuota e EXISTS rende Vero altrimenti (se la query interna è vuota) rende Falso. La query mostra tutti i giocatori che hanno svolto gare. WHERE NOT EXISTS ( WHERE Giocatori.ID = Gare.Player); La query mostra tutti i giocatori che NON hanno svolto gare. AS G1 WHERE NOT EXISTS ( AS G2 WHERE G1.Gioco = G2.Gioco AND G2.Livello > G1.Livello); La query mostra le gare coi livelli migliori per ciascun Gioco. ANNIDARE IN PIÙ LIVELLI Se necessario è possibile nidificare le query in più livelli, ovvero inserire in una query interna un altra query ancora più interna.
LEZIONE14 SQL ANNIDAMENTI PAG. 5 / 5 PROF. ANDREA ZOCCHEDDU ESEMPIO 1. AS G1 WHERE ID IN ( SELECT Player WHERE Gioco IN ( SELECT Gioco WHERE Player <> G1.ID ) ); La query mostra tutti i giocatori che hanno fatto giochi a cui ha giocato anche qualcun altro. ESEMPIO 2. AS G1 WHERE NOT EXISTS( as G2 WHERE NOT EXISTS( AS G3 WHERE G3.Game = G2.Game AND G3.Player <> G1.ID ) ); Non ho idea di cosa faccia questa query Chi mi può aiutare? INDICE DELLA LEZIONE DB di riferimento... 1 annidamento nella select (sconsigliato)... 1 annidamento nella from (sconsigliato)... 2 Annidamento nella clausola where (ammesso)... 2 Annidamento e Operatori di Confronto... 2 =, <>, >, <, >=, <=... 3 in (e not in)... 3 Operatore Exists... 4 Annidare in più livelli... 4 Esempio 1.... 5 Esempio 2.... 5