Complexo juntar com o grupo-by / cláusula having aninhada?
Pergunta
I última análise, precisamos de uma lista de registros de "importação" que incluem "álbum" registros que apenas têm uma "canção" cada um.
Isto é o que eu estou usando agora:
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)
);
O SELECT aninhada (com a junção) é super rápido, mas o externo "In" cláusula é dolorosamente lento.
Eu tentei fazer a consulta inteira uma única (sem nidificação) participar, mas ran problemas com as cláusulas do grupo / ter. O melhor que eu podia fazer era uma lista de registros de "importação" com ingênuos, que não é aceitável.
Existe uma maneira mais elegante para compor esta consulta?
Solução
Como é isso?
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;
Na maioria dos sistemas de banco de dados, o JOIN funciona um perdido mais rápido do que fazendo um WHERE ... IN.
Outras dicas
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
(Você pode não precisar de incluir o COUNT
na lista SELECT
si. SQL Server não exigir isso, mas é possível que um RDBMS poder diferente.)
Não testado:
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)
Todas as três técnicas sugerido deve ser mais rápido do que o seu ONDE:
- existe com uma subconsulta relacionada (GBN)
- Subquery que é interior unidas (achinda99)
- Inner Juntando todas as três tabelas (luke)
(Todos devem trabalhar, também ..., então +1 para todos eles. Por favor, deixe-nos saber se um deles não funciona!)
Qual deles se transforma realmente a ser o mais rápido, depende de seus dados e o plano de execução. Mas um exemplo interessante de maneiras diferentes para expressar a mesma coisa em SQL.
Eu tentei fazer toda a consulta de um única (sem nidificação) participar, mas correu para problemas com o grupo / possuindo cláusulas.
Você pode juntar-se subconsulta usando CTE (Common Table Expression) se você estiver usando SQL Server versão 2005/2008
Tanto quanto eu sei, CTE é simplesmente uma expressão que funciona como uma visão virtual que funciona apenas um um único selecione declaração - Então, você será capaz de fazer o seguinte. Eu costumo encontrar usando CTE para melhorar a consulta de desempenho também.
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