Complexe rejoindre avec le groupe par imbriqué / clause having?
Question
J'ai besoin en fin de compte une liste de « importer » des documents qui incluent « album » dossiers qui ont seulement une « chanson » chacun.
est ce que je suis maintenant avec:
select i.id, i.created_at
from imports i
where i.id in (
select a.import_id
from albums a inner join songs s on a.id = s.album_id
group by a.id having 1 = count(s.id)
);
La sélection (avec la jointure) imbriquée est ultra-rapide, mais l'extérieur "Dans" clause est atrocement lent.
J'ai essayé de faire toute la requête d'un seul (pas d'imbrication), mais couru rejoindre des problèmes avec les clauses de groupe / ayant. Le mieux que je pouvais faire était une liste des dossiers « d'importation » avec dupes ce qui est inacceptable.
Y at-il une façon plus élégante pour composer cette requête?
La solution
Comment ce?
SELECT i.id,
i.created_at
FROM imports i
INNER JOIN (SELECT a.import_id
FROM albums a
INNER JOIN songs s
ON a.id = s.album_id
GROUP BY a.id
HAVING Count(* ) = 1) AS TEMP
ON i.id = TEMP.import_id;
Dans la plupart des systèmes de base de données, les œuvres REJOIGNEZ une perte plus rapide que de faire WHERE ... IN.
Autres conseils
SELECT i.id, i.created_at, COUNT(s.album_id)
FROM imports AS i
INNER JOIN albums AS a
ON i.id = a.import_id
INNER JOIN songs AS s
ON a.id = s.album_id
GROUP BY i.id, i.created_at
HAVING COUNT(s.album_id) = 1
(Vous ne pourriez pas besoin d'inclure le COUNT
dans la liste SELECT
lui-même. SQL Server ne nécessite pas, mais il est possible qu'une puissance SGBDR différente.)
Untested:
select
i.id, i.created_at
from
imports i
where
exists (select *
from
albums a
join
songs s on a.id = s.album_id
where
a.import_id = i.id
group by
a.id
having
count(*) = 1)
ou
select
i.id, i.created_at
from
imports i
where
exists (select *
from
albums a
join
songs s on a.id = s.album_id
group by
a.import_id, a.id
having
count(*) = 1 AND a.import_id = i.id)
Les trois techniques sugested devraient être plus rapide que WHERE IN:
- coexiste avec une sous-requête associée (GBN)
- sous-requête qui est interne joint (achinda99)
- intérieur joindre les trois tableaux (luke)
(Tout le monde devrait travailler aussi ..., donc +1 pour tous. S'il vous plaît laissez-nous savoir si l'un d'eux ne fonctionne pas!)
Lequel se fait avéré être le plus rapide, dépend de vos données et le plan d'exécution. Mais un exemple intéressant de façons différentes pour exprimer la même chose dans SQL.
J'ai essayé de faire l'une de toute requête unique (pas de nidification) se joindre, mais a couru dans problèmes avec le groupe / ayant clauses.
Vous pouvez vous joindre à l'aide de CTE sous_requête (Common Table Expression) si vous utilisez la version SQL Server 2005/2008
Pour autant que je sache, CTE est tout simplement une expression qui fonctionne comme une vue virtuelle qui fonctionne seul un instruction select - Vous serez en mesure de faire ce qui suit. Je trouve généralement en utilisant CTE pour améliorer les performances des requêtes ainsi.
with AlbumSongs as (
select a.import_id
from albums a inner join songs s on a.id = s.album_id
group by a.id
having 1 = count(s.id)
)
select i.id, i.created_at
from imports i
inner join AlbumSongs A on A.import_id = i.import_id