Complex join with nested group-by/having clause?

孤人 提交于 2019-12-22 00:06:08

问题


I ultimately need a list of "import" records that include "album" records which only have one "song" each.

This is what I'm using now:

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)
);

The nested select (with the join) is blazing fast, but the external "in" clause is excruciatingly slow.

I tried to make the entire query a single (no nesting) join but ran into problems with the group/having clauses. The best I could do was a list of "import" records with dupes, which is not acceptable.

Is there a more elegant way to compose this query?


回答1:


How's this?

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; 

In most database systems, the JOIN works a lost faster than doing a WHERE ... IN.




回答2:


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

(You might not need to include the COUNT in the SELECT list itself. SQL Server doesn't require it, but it's possible that a different RDBMS might.)




回答3:


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)

OR

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)



回答4:


All three sugested techniques should be faster than your WHERE IN:

  1. Exists with a related subquery (gbn)
  2. Subquery that is inner joined (achinda99)
  3. Inner Joining all three tables (luke)

(All should work, too ..., so +1 for all of them. Please let us know if one of them does not work!)

Which one actually turns out to be the fastest, depends on your data and the execution plan. But an interesting example of different ways for expressing the same thing in SQL.




回答5:


I tried to make the entire query a single (no nesting) join but ran into problems with the group/having clauses.

You can join subquery using CTE (Common Table Expression) if you are using SQL Server version 2005/2008

As far as I know, CTE is simply an expression that works like a virtual view that works only one a single select statement - So you will be able to do the following. I usually find using CTE to improve query performance as well.

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


来源:https://stackoverflow.com/questions/579175/complex-join-with-nested-group-by-having-clause

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!