2016-03-01 26 views
1

我有兩個表:parcelstructure,它們之間有一對多的關係:structure.parcel_id指向parcel.id從GROUP BY中選擇單個和唯一的單行

我想選擇所有的單個結構。我目前的解決方案的工作,但很怪誕:

SELECT 
max(column_1), 
max(column_2), 
max(column_3), 
... 
(twenty+ columns) 

FROM structure 
GROUP BY parcel_id 
HAVING count(structure.id) = 1; 

因爲structure.id是不可爲空及以上HAVING條款,每個組都有,顧名思義,只有一排它。不幸的是Postgres沒有意識到這一點,所以如果我說:

SELECT *  
FROM structure 
GROUP BY parcel_id 
HAVING count(structure.id) = 1; 

然後,我得到預期的錯誤,需要使用列的聚合函數。我用任意的max()函數解決了這個問題,但是這對於試圖理解代碼的其他人來說很混亂,並且迫使我明確列出所有的列,這意味着只要列是添加。 (其中,不幸的是,亦常發生在我的環境。)

我有這個替代的解決方案,解決了我的大部分問題:

SELECT * FROM STRUCTURE 
WHERE id IN (
    SELECT 
     max(id) as id 
    FROM structure 
    GROUP by structure.parcel_id 
    HAVING count(structure.id) = 1 
    ); 

但這顯然增加不必要的緩慢向我詢問我」 d喜歡避免因爲查詢的頻率和表的大小。

This question與我所要求的非常相似,但它將抓住每個組的第一行,而不是單數組的第一行(也是唯一一行)。

有沒有一種優雅的方式來解決這個問題?每個請求

樣本數據:

structure表:

id | parcel_id | column_1 | column_2 | ... 
------------------------------------------ 
1 | 536  | ... | ....  | ... 
2 | 536  | ... | ....  | ... 
3 | 537  | ... | ....  | ... 
4 | 538  | ... | ....  | ... 
5 | 538  | ... | ....  | ... 
6 | 539  | ... | ....  | ... 
7 | 540  | ... | ....  | ... 
8 | 541  | ... | ....  | ... 
9 | 541  | ... | ....  | ... 

期望的結果:

id | parcel_id | column_1 | column_2 | ... 
------------------------------------------ 
3 | 537  | ... | ....  | ... 
6 | 539  | ... | ....  | ... 
7 | 540  | ... | ....  | ... 

注意,537,539,和540是唯一parcel_id的那不要重複。

兩個表都有〜150萬行和〜25列。

+0

能否請您發佈一些有代表性的樣本數據和期望的結果。 –

+0

所以,「結構」表有150萬行。 'parcel'表中有多少行? 「結構」表中有多少個地塊只有一個對應的行?換句話說,最終查詢返回多少行? –

+0

我不熟悉SQL,因此在評論中張貼我的建議。你可以使用self join作爲這個'SELECT S1。* FROM STRUCTURE S1,STRUCTURE S2 WHERE S1.parcel_id = S2.parcel_id GROUP BY S2.parcel_id HAVING count(S2.parcel_id)= 1' –

回答

1

如何使用窗口函數?

SELECT s.*  
FROM (SELECT s.*, COUNT(*) OVER (PARTITION BY parcel_id) as cnt 
     FROM structure s 
    ) s 
WHERE cnt = 1; 

然而,更有效的方法可能是:

select s.* 
from structure s 
where not exists (select 1 
        from structure s2 
        where s2.parcel_id = s.parcel_id and s2.id<> s.id 
       ); 

特別地,這可以在structure(parcel_id, id)採取的索引的優點。

+0

你的第二種方法很聰明!我喜歡它,但經過一些(儘管很粗糙)的基準測試和查詢計劃分析後,它看起來會比我的怪誕解決方案慢。我希望能夠在速度方面至少達到匹配的解決方案。 – lnhubbell

+0

@inhubbell。 。 。你有適當的索引嗎?我希望第二條消息能夠與索引相當快。 –

1

這應該是相當快:

SELECT s.* 
FROM (
    SELECT parcel_id 
    FROM structure 
    GROUP BY 1 
    HAVING count(*) = 1 
    ) s1 
JOIN structure s USING (parcel_id); 

所有你需要的是(parcel_id)指數。

  • 由於查詢僅限於獨特parcel_id,沒有必要涉及的子查詢id。所以我們可以從(parcel_id)的簡單索引中獲得一個index-only scan - 併爲連接使用相同的索引。

  • 這個連接應該比IN快一點,並帶有一個大的子查詢。 (儘管它們大多數導致現代Postgres中的查詢計劃相同)。

  • count(*)count(<expression>)要快一點,因爲只有行的存在才能建立。

旁白:

@Gordon's 2nd queryNOT EXISTS反半連接應該是快了。您只需要(parcel_id, id)上的多列索引。

question you linked to適用於SQL Server。下面是Postgres的一個更相關的相關的問題: