J'essaie d'apprendre SQL et j'ai du mal à comprendre les déclarations EXISTS. Je suis tombé sur cette citation sur "existe" et je ne comprends pas quelque chose:
En utilisant l'opérateur existe, votre sous-requête peut renvoyer zéro, une ou plusieurs lignes, et la condition vérifie simplement si la sous-requête a renvoyé des lignes. Si vous regardez la clause select de la sous-requête, vous verrez qu'elle se compose d'un seul littéral (1); étant donné que la condition de la requête contenant n'a besoin que de savoir combien de lignes ont été renvoyées, les données réelles renvoyées par la sous-requête ne sont pas pertinentes.
Ce que je ne comprends pas, c'est comment la requête externe sait quelle ligne la sous-requête vérifie? Par exemple:
SELECT *
FROM suppliers
WHERE EXISTS (select *
from orders
where suppliers.supplier_id = orders.supplier_id);
Je comprends que si l'ID du fournisseur et la table des commandes correspondent, la sous-requête retournera true et toutes les colonnes de la ligne correspondante dans la table des fournisseurs seront générées. Ce que je n'obtiens pas, c'est comment la sous-requête communique quelle ligne spécifique (disons la ligne avec l'ID de fournisseur 25) doit être imprimée si seulement un vrai ou un faux est retourné.
Il me semble qu'il n'y a pas de relation entre la requête externe et la sous-requête.
Que pensez-vous que fait la clause WHERE dans l'exemple EXISTS? Comment arrivez-vous à cette conclusion lorsque la référence SUPPLIERS n'est pas dans les clauses FROM ou JOIN de la clause EXISTS?
EXISTS prend la valeur TRUE / FALSE et se termine par TRUE lors de la première correspondance des critères - c'est pourquoi il peut être plus rapide que
IN
. Sachez également que la clause SELECT dans un EXISTS est ignorée - IE:SELECT s.* FROM SUPPLIERS s WHERE EXISTS (SELECT 1/0 FROM ORDERS o WHERE o.supplier_id = s.supplier_id)
... devrait atteindre une division par zéro erreur, mais ce ne sera pas le cas. La clause WHERE est l'élément le plus important d'une clause EXISTS.
Sachez également qu'un JOIN ne remplace pas directement EXISTS, car il y aura des enregistrements parent en double s'il y a plus d'un enregistrement enfant associé au parent.
la source
EXISTS
sorties, retournant TRUE sur la première correspondance - car le fournisseur existe au moins une fois dans la table ORDERS. Si vous souhaitez voir la duplication des données FOURNISSEUR en raison de la présence de plusieurs relations enfants dans ORDERS, vous devez utiliser un JOIN. Mais la plupart ne veulent pas de cette duplication, et l'exécution de GROUP BY / DISTINCT est susceptible d'ajouter une surcharge à la requête.EXISTS
est plus efficace queSELECT DISTINCT ... FROM SUPPLIERS JOIN ORDERS ...
sur SQL Server, n'a pas testé sur Oracle ou MySQL ces derniers temps.Vous pouvez produire des résultats identiques utilisant
JOIN
,EXISTS
,IN
ouINTERSECT
:SELECT s.supplier_id FROM suppliers s INNER JOIN (SELECT DISTINCT o.supplier_id FROM orders o) o ON o.supplier_id = s.supplier_id SELECT s.supplier_id FROM suppliers s WHERE EXISTS (SELECT * FROM orders o WHERE o.supplier_id = s.supplier_id) SELECT s.supplier_id FROM suppliers s WHERE s.supplier_id IN (SELECT o.supplier_id FROM orders o) SELECT s.supplier_id FROM suppliers s INTERSECT SELECT o.supplier_id FROM orders o
la source
Si vous aviez une clause where qui ressemblait à ceci:
WHERE id in (25,26,27) -- and so on
vous pouvez facilement comprendre pourquoi certaines lignes sont renvoyées et d'autres non.
Lorsque la clause where est comme ceci:
WHERE EXISTS (select * from orders where suppliers.supplier_id = orders.supplier_id);
cela signifie simplement: retourner les lignes qui ont un enregistrement existant dans la table des commandes avec le même identifiant.
la source
Modèle de table de base de données
Supposons que nous ayons les deux tables suivantes dans notre base de données, qui forment une relation de table un-à-plusieurs.
La
student
table est le parent et lastudent_grade
est la table enfant car elle a une colonne de clé étrangère student_id faisant référence à la colonne de clé primaire id dans la table student.Le
student table
contient les deux enregistrements suivants:| id | first_name | last_name | admission_score | |----|------------|-----------|-----------------| | 1 | Alice | Smith | 8.95 | | 2 | Bob | Johnson | 8.75 |
Et, le
student_grade
tableau stocke les notes que les élèves ont reçues:| 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 EXISTE
Disons que nous voulons obtenir tous les élèves qui ont reçu une note de 10 en classe de mathématiques.
Si nous ne sommes intéressés que par l'identifiant de l'étudiant, nous pouvons exécuter une requête comme celle-ci:
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
Mais, l'application est intéressée à afficher le nom complet d'un
student
, pas seulement l'identifiant, nous avons donc également besoin d'informations de lastudent
table.Afin de filtrer les
student
enregistrements qui ont une note de 10 en mathématiques, nous pouvons utiliser l'opérateur SQL EXISTS, comme ceci: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
Lors de l'exécution de la requête ci-dessus, nous pouvons voir que seule la ligne Alice est sélectionnée:
| id | first_name | last_name | |----|------------|-----------| | 1 | Alice | Smith |
La requête externe sélectionne les
student
colonnes de lignes que nous souhaitons renvoyer au client. Cependant, la clause WHERE utilise l'opérateur EXISTS avec une sous-requête interne associée.L'opérateur EXISTS renvoie true si la sous-requête renvoie au moins un enregistrement et false si aucune ligne n'est sélectionnée. Le moteur de base de données n'a pas à exécuter entièrement la sous-requête. Si un seul enregistrement correspond, l'opérateur EXISTS renvoie true et l'autre ligne de requête associée est sélectionnée.
La sous-requête interne est corrélée car la colonne student_id de la
student_grade
table est mise en correspondance avec la colonne id de la table externe Student .la source
EXIST
uniquement avec la sous-requête corrélée? Je jouais avec une requête contenant seulement 1 table, commeSELECT id FROM student WHERE EXISTS (SELECT 1 FROM student WHERE student.id > 1)
. Je sais que ce que j'ai écrit pourrait être réalisé par une simple requête WHERE, mais je l'utilisais juste pour comprendre EXISTS. J'ai toutes les lignes. Est-ce bien dû au fait que je n'ai pas utilisé de sous-requête corrélée? Merci.EXISTS signifie que la sous-requête renvoie au moins une ligne, c'est vraiment ça. Dans ce cas, il s'agit d'une sous-requête corrélée car elle vérifie le supplier_id de la table externe par rapport au supplier_id de la table interne. Cette requête dit, en effet:
CHOISIR tous les fournisseurs Pour chaque ID fournisseur, voir si une commande existe pour ce fournisseur Si le fournisseur n'est pas présent dans la table des commandes, supprimer le fournisseur des résultats RETOUR tous les fournisseurs qui ont des lignes correspondantes dans la table des commandes
Vous pouvez faire la même chose dans ce cas avec un INNER JOIN.
SELECT suppliers.* FROM suppliers INNER JOIN orders ON suppliers.supplier_id = orders.supplier_id;
Le commentaire des poneys est correct. Vous devez effectuer un regroupement avec cette jointure ou sélectionner distinct en fonction des données dont vous avez besoin.
la source
Ce que vous décrivez est une soi-disant requête avec une sous-requête corrélée .
(En général) c'est quelque chose que vous devriez essayer d'éviter en écrivant la requête en utilisant une jointure à la place:
SELECT suppliers.* FROM suppliers JOIN orders USING supplier_id GROUP BY suppliers.supplier_id
Dans le cas contraire, la sous-requête sera exécutée pour chaque ligne de la requête externe.
la source
orders
qui correspond à la condition de jointure.