web-dev-qa-db-fra.com

Sous-requête MYSQL dans les colonnes de l'instruction select

Comment les sous-requêtes dans le champ de colonne de select (projection) sont-elles associées au résultat de la requête principale? sous la forme:

 SELECT id,email,(SELECT name From Names WHERE Names.id=Users.id) as name
 FROM Users

La sous-requête est-elle exécutée une fois par ligne, à partir de la sortie de SELECT id,email FROM Users, et donc, on devrait utiliser LIMIT 1 sur la sous-requête (puisqu'une seule ligne de la sous-requête peut être associée à une ligne de la requête principale), ou la sous-requête s'exécute-t-elle une fois, puis chaque résultat est associé à la ligne correspondante de SELECT id,email FROM Users, un peu comme la jointure équivalente: SELECT id,email,name FROM Users JOIN Names ON Users.id=Names.id

5
Daniel Valland

dans une situation idéale, lorsque Names.id = Users.id ne renvoie qu'un seul enregistrement, les deux requêtes sont identiques.

La différence quand ce n'est pas vrai.

SELECT id,email,(SELECT name From Names WHERE Names.id=Users.id) as name
 FROM Users

arrêtera le travail et renverra une erreur, vous devrez donc ajouter la classe LIMIT

SELECT id,email,(SELECT name From Names WHERE Names.id=Users.id ORDER BY something LIMIT 1) as name
 FROM Users

en même temps interroger

SELECT id,email,name FROM Users JOIN Names ON Users.id=Names.id

continuer le travail sans erreurs, cette requête renvoie toutes les lignes des noms liés aux utilisateurs

Dans certains autres cas, lorsque vous n'attendez qu'un seul nom, vous devrez ajouter des conditions GROUP BY

SELECT id,email,name FROM Users JOIN Names ON Users.id=Names.id GROUP BY Users.id

Mais cette condition pourrait renvoyer un nom imprévu à partir de Names (et ce n'est pas une construction 100% légale pour SQL), et vous auriez encore besoin d'ajouter 1 niveau supplémentaire de JOIN avec des tables dérivées, et quelque temps cela pourrait être une construction moche,

ainsi, vous pouvez toujours comparer la forme de requête la plus correcte pour le cas sélectionné, exemple simple:

SELECT t1.id,t1.email,t2.name FROM Users t1 JOIN 
(SELECT id, name FROM Names n1 INNER JOIN 
(SELECT MAX(dateregistered) as dateregistered, id FROM Names GROUP BY id) n2 ON n1.id=n2.id AND n1.dateregistered=n2.dateregistered) t2
ON t1.id = t2.id

renverra le même résultat que:

SELECT id,email,(SELECT name From Names WHERE Names.id=Users.id ORDER BY dateregistered DESC LIMIT 1) as name
FROM Users

Ajouter: L'exemple avec les noms ne ressemble pas à une situation réaliste, mais réelle - lorsque vous avez besoin d'une demande, pas un nom qui est vraiment 1 par personne, mais l'adresse postale réelle du client avec 10 ans d'histoire. Il peut avoir 20 adresses, et vous devez renvoyer le plus de ressentiment

4
a_vlad

D'après mon expérience Oui, votre sous-requête s'exécutera pour chaque ligne de votre requête externe.

De plus, comme vous n'utilisez aucune forme de regroupement/agrégation dans votre sous-requête, il n'y a aucune garantie sur la ligne que cette requête renverra.

En plus, cette requête est mieux écrite en tant que telle:

SELECT u.id,u.email,n.name
FROM Users u
JOIN Names n
    ON n.id = u.id

Comme indiqué ci-dessous, si les utilisateurs de table et les noms de table ne possèdent pas une vraie relation 1 à 1. il est possible qu'une ligne dans les utilisateurs ait plusieurs lignes dans les noms avec le même identifiant. Si tel est le cas, vous pouvez apporter les modifications suivantes à la requête pour en tenir compte.

SELECT u.id,u.email,n.name
FROM Users u
RIGHT JOIN Names n
    ON n.id = u.id
WHERE u.id IS NOT NULL
LIMIT 1

Cela vous donnera chaque ligne de la table Users, qui possède un identifiant correspondant dans la table Names. Mais limitez les réponses à 1. Gardez à l'esprit que sans LIMIT 1, cela répertorierait les e-mails en double, si pour une raison quelconque, plusieurs entrées de la table Noms ont le même identifiant.

0
DarbyM