PostgreSQL - Echec de la sous-requête corrélée?
-
06-07-2019 - |
Question
J'ai une requête comme celle-ci:
SELECT t1.id,
(SELECT COUNT(t2.id)
FROM t2
WHERE t2.id = t1.id
) as num_things
FROM t1
WHERE num_things = 5;
Le but est d’obtenir l’id de tous les éléments qui apparaissent 5 fois dans l’autre tableau. Cependant, je reçois cette erreur:
ERROR: column "num_things" does not exist
SQL state: 42703
Je fais probablement quelque chose de stupide ici, car je suis un peu nouveau dans les bases de données. Y at-il un moyen de résoudre cette requête afin que je puisse accéder à num_things
? Sinon, y a-t-il un autre moyen d'obtenir ce résultat?
La solution
Je pense que vous pourriez simplement réécrire votre requête comme suit:
SELECT t1.id
FROM t1
WHERE (SELECT COUNT(t2.id)
FROM t2
WHERE t2.id = t1.id
) = 5;
Autres conseils
Quelques points importants sur l'utilisation de SQL:
- Vous ne pouvez pas utiliser d'alias de colonne dans la clause WHERE, mais dans la clause HAVING. C'est la cause de l'erreur que vous avez.
- Vous pouvez mieux compter en utilisant JOIN et GROUP BY qu'en utilisant des sous-requêtes corrélées. Ce sera beaucoup plus rapide.
- Utilisez la clause HAVING pour filtrer les groupes.
Voici comment j'écrirais cette requête:
SELECT t1.id, COUNT(t2.id) AS num_things
FROM t1 JOIN t2 USING (id)
GROUP BY t1.id
HAVING num_things = 5;
Je réalise que cette requête peut ignorer le JOIN
avec t1, ??comme dans la solution de Charles Bretana. Mais je suppose que vous voudrez peut-être que la requête inclue d'autres colonnes de t1.
Re: la question dans le commentaire:
La différence est que la clause WHERE
est évaluée sur des lignes, avant que GROUP BY
ne réduise les groupes que sur une seule ligne. La clause HAVING
est évaluée après la formation des groupes. Ainsi, vous ne pouvez pas, par exemple, changer le COUNT ()
d'un groupe en utilisant HAVING
; vous ne pouvez exclure que le groupe lui-même.
SELECT t1.id, COUNT(t2.id) as num
FROM t1 JOIN t2 USING (id)
WHERE t2.attribute = <value>
GROUP BY t1.id
HAVING num > 5;
Dans la requête ci-dessus, WHERE
filtre pour les lignes correspondant à une condition et HAVING
pour les groupes comptant au moins cinq chiffres.
La plupart des gens sont perplexes lorsqu'ils ne possèdent pas de clause GROUP BY
, de sorte qu'il semble comme HAVING
et < code> WHERE sont interchangeables.
WHERE
est évalué avant les expressions de la liste de sélection. Cela n’est peut-être pas évident, car la syntaxe SQL place la liste de sélection en premier. Vous pouvez donc économiser beaucoup de calculs coûteux en utilisant WHERE
pour restreindre les lignes.
SELECT <expensive expressions>
FROM t1
HAVING primaryKey = 1234;
Si vous utilisez une requête semblable à celle ci-dessus, les expressions de la liste de sélection sont calculées pour chaque ligne , uniquement pour écarter la plupart des résultats en raison du HAVING
. état. Cependant, la requête ci-dessous calcule l'expression uniquement pour la seule ligne correspondant à la condition WHERE
.
SELECT <expensive expressions>
FROM t1
WHERE primaryKey = 1234;
Donc, pour récapituler, les requêtes sont exécutées par le moteur de base de données selon une série d'étapes:
- Génère un ensemble de lignes à partir de table (s), y compris les lignes générées par
JOIN
. - Évaluez les conditions
WHERE
par rapport à l'ensemble de lignes, en filtrant les lignes qui ne correspondent pas. - Calculez les expressions dans la liste de sélection pour chacune des lignes.
- Appliquer des alias de colonne (notez qu'il s'agit d'une étape distincte, ce qui signifie que vous ne pouvez pas utiliser d'alias dans les expressions de la liste de sélection).
- Condensez les groupes sur une seule ligne par groupe, conformément à la clause
GROUP BY
. - Évaluez les conditions
HAVING
par rapport aux groupes, en filtrant les groupes qui ne correspondent pas. - Trier le résultat en fonction de la clause
ORDER BY
.
Toutes les autres suggestions fonctionneraient, mais pour répondre à votre question de base, il suffirait d'écrire
SELECT id From T2
Group By Id
Having Count(*) = 5
Je voudrais mentionner que dans PostgreSQL, il n'y a aucun moyen d'utiliser une colonne avec alias dans une clause.
c'est-à-dire
SELECT usr_id AS my_id FROM utilisateur HAVING my_id = 1
Ne fonctionnera pas.
Un autre exemple qui ne fonctionnera pas:
SELECT su.usr_id AS my_id, COUNT (*) AS val de sys_user AS SU GROUP BY su.usr_id HAVING val > = 1
Il y aura la même erreur: la colonne val n'est pas connue.
Je souligne ceci parce que Bill Karwin a écrit quelque chose qui n’est pas vraiment vrai pour Postgres:
"Vous ne pouvez pas utiliser d'alias de colonne dans la clause WHERE, mais dans la clause HAVING. C’est la cause de l’erreur que vous avez eue. "
essayez ceci
SELECT t1.id,
(SELECT COUNT(t2.id) as myCount
FROM t2
WHERE t2.id = t1.id and myCount=5
) as num_things
FROM t1