Questa è un'ottima domanda, quindi ho deciso di scrivere un articolo molto dettagliato su questo argomento sul mio blog.
Modello di tabella del database
Supponiamo di avere le seguenti due tabelle nel nostro database, che formano una relazione da una a molte tabelle.
La student
tabella è padre e student_grade
è la tabella figlio poiché ha una colonna Chiave esterna student_id che fa riferimento alla colonna Chiave primaria id nella tabella studente.
Il student table
contiene i seguenti due record:
| id | first_name | last_name | admission_score |
|----|------------|-----------|-----------------|
| 1 | Alice | Smith | 8.95 |
| 2 | Bob | Johnson | 8.75 |
E la student_grade
tabella memorizza i voti che gli studenti hanno ricevuto:
| id | class_name | grade | student_id |
|----|------------|-------|------------|
| 1 | Math | 10 | 1 |
| 2 | Math | 9.5 | 1 |
| 3 | Math | 9.75 | 1 |
| 4 | Science | 9.5 | 1 |
| 5 | Science | 9 | 1 |
| 6 | Science | 9.25 | 1 |
| 7 | Math | 8.5 | 2 |
| 8 | Math | 9.5 | 2 |
| 9 | Math | 9 | 2 |
| 10 | Science | 10 | 2 |
| 11 | Science | 9.4 | 2 |
SQL EXISTS
Diciamo che vogliamo ottenere tutti gli studenti che hanno ricevuto un voto di 10 in classe di matematica.
Se siamo interessati solo all'identificatore dello studente, possiamo eseguire una query come questa:
SELECT
student_grade.student_id
FROM
student_grade
WHERE
student_grade.grade = 10 AND
student_grade.class_name = 'Math'
ORDER BY
student_grade.student_id
Ma l'applicazione è interessata a visualizzare il nome completo di un student
, non solo l'identificatore, quindi abbiamo bisogno anche di informazioni dalla student
tabella.
Per filtrare i student
record che hanno un grado 10 in matematica, possiamo usare l'operatore EXISTS SQL, in questo modo:
SELECT
id, first_name, last_name
FROM
student
WHERE EXISTS (
SELECT 1
FROM
student_grade
WHERE
student_grade.student_id = student.id AND
student_grade.grade = 10 AND
student_grade.class_name = 'Math'
)
ORDER BY id
Quando eseguiamo la query sopra, possiamo vedere che è selezionata solo la riga Alice:
| id | first_name | last_name |
|----|------------|-----------|
| 1 | Alice | Smith |
La query esterna seleziona le student
colonne delle righe che siamo interessati a restituire al client. Tuttavia, la clausola WHERE utilizza l'operatore EXISTS con una subquery interna associata.
L'operatore EXISTS restituisce true se la sottoquery restituisce almeno un record e false se non è selezionata alcuna riga. Il motore di database non deve eseguire interamente la sottoquery. Se viene abbinato un singolo record, l'operatore EXISTS restituisce true e viene selezionata l'altra riga della query associata.
La sottoquery interna è correlata perché la colonna student_id della student_grade
tabella è confrontata con la colonna id della tabella studente esterna.
SQL NON ESISTE
Consideriamo che vogliamo selezionare tutti gli studenti che non hanno un voto inferiore a 9. Per questo, possiamo usare NOT EXISTS, che annulla la logica dell'operatore EXISTS.
Pertanto, l'operatore NOT EXISTS restituisce true se la sottoquery sottostante non restituisce alcun record. Tuttavia, se un singolo record corrisponde alla sottoquery interna, l'operatore NOT EXISTS restituirà false e l'esecuzione della sottoquery può essere interrotta.
Per abbinare tutti i record degli studenti a cui non è associato student_grade con un valore inferiore a 9, possiamo eseguire la seguente query SQL:
SELECT
id, first_name, last_name
FROM
student
WHERE NOT EXISTS (
SELECT 1
FROM
student_grade
WHERE
student_grade.student_id = student.id AND
student_grade.grade < 9
)
ORDER BY id
Quando eseguiamo la query sopra, possiamo vedere che solo il record di Alice è abbinato:
| id | first_name | last_name |
|----|------------|-----------|
| 1 | Alice | Smith |
Pertanto, il vantaggio di utilizzare gli operatori SQL EXISTS e NOT EXISTS è che l'esecuzione della sottoquery interna può essere arrestata fino a quando viene trovato un record corrispondente.