2009-02-23 50 views
1

我最終需要一個包含「專輯」 記錄的「導入」記錄列表,每個記錄只有一首「歌曲」。使用嵌套group by/having子句進行復雜連接?

這是我現在使用什麼:

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

嵌套選擇(與連接)的速度極快,但外部 「在」條款是速度奇慢。

我試圖讓整個查詢成爲單個(無嵌套)連接,但運行 時出現了group/having子句的問題。我能做的最好的事情是 帶有模糊的「導入」記錄列表,這是不可接受的。

是否有更優雅的方式來組成這個查詢?

+0

你會指定RDBMS嗎? – Sung 2009-02-23 21:14:31

回答

7

這個怎麼樣?

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; 

在大多數數據庫系統中,JOIN的工作速度比執行WHERE ... IN要快。

+0

這已經夠接近了。我不得不添加「通過i.id,i.created_at組」來達到「不受騙」的要求(見原文)。謝謝。 – 2009-02-23 22:29:59

2

未經測試:

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

(您可能不需要包括在SELECT列表本身。SQL Server不需要它COUNT,但它可能是一個不同的RDBMS的威力。)

1

所有這三個sugested技術應該比你的WHERE更快:

  1. 存在具有相關子查詢(GBN)
  2. 子查詢是內加入(achinda99)
  3. 內側連接所有三個表(路)

(所有應該工作,太......,所以+1爲所有這些。請讓我們知道,如果其中一個不工作!)

哪一個實際上是最快的,取決於您的數據和執行計劃。但是用SQL表達同樣的東西的一個有趣的例子。

1

我試圖讓整個查詢 單(不嵌套)加入,但跑進 問題跟團/有 條款。

你可以,如果你使用的是SQL Server版本2005/2008

據我所知使用CTE(公共表表達式)加入子查詢,CTE很簡單,就像一個可行的虛擬視圖表達只有一個選擇聲明 - 所以你將能夠做到以下幾點。 我通常使用CTE來提高查詢性能。

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