2010-07-22 127 views
5

好的,所以標題有點複雜。這基本上是一個最大的每組類型的問題,但我不能爲我的生活弄清楚。Postgres,table1左連接table2,表1中每個ID只有1行

我有一個表,user_stats:

------------------+---------+--------------------------------------------------------- 
id    | bigint | not null default nextval('user_stats_id_seq'::regclass) 
user_id   | bigint | not null 
datestamp  | integer | not null 
post_count  | integer | 
friends_count | integer | 
favourites_count | integer | 
Indexes: 
    "user_stats_pk" PRIMARY KEY, btree (id) 
    "user_stats_datestamp_index" btree (datestamp) 
    "user_stats_user_id_index" btree (user_id) 
Foreign-key constraints: 
    "user_user_stats_fk" FOREIGN KEY (user_id) REFERENCES user_info(id) 

我想要得到的統計信息通過最新的郵戳每個ID。這是一個較大的表,在41米行附近的地方,所以我創建的user_id的臨時表,LAST_DATE使用:

CREATE TEMP TABLE id_max_date AS 
    (SELECT user_id, MAX(datestamp) AS date FROM user_stats GROUP BY user_id); 

的問題是,郵戳是不是唯一的,因爲可以有不止一天中的1次統計更新(本應該是一個真正的時間戳,但設計這個的人是一個白癡,現在還有太多數據可以回溯)。因此,一些標識有當我做JOIN多行:

SELECT user_stats.user_id, user_stats.datestamp, user_stats.post_count, 
     user_stats.friends_count, user_stats.favorites_count 
    FROM id_max_date JOIN user_stats 
    ON id_max_date.user_id=user_stats.user_id AND date=datestamp; 

如果我這樣做,因爲子查詢我想我可能會限制1,但我一直聽說這些都是可怕的效率低下。思考?

+0

「......我一直聽說這些效率非常低下。」不要被捲入貨物邪教! 'EXPLAIN'是你的朋友!試試看看查詢優化器可以爲你做什麼。 – Charles 2010-07-22 04:17:47

回答

23

DISTINCT ON是你的朋友。

select distinct on (user_id) * from user_stats order by datestamp desc; 
+0

這正是我想要的,它特定於postgres,所以不是很理想,但是我會在它周圍留下一個註釋並隨之移動。謝謝! – Peck 2010-07-22 04:40:23

+0

@Peck - 我認爲DISTINCT ON是最便利的postgres-isms之一。我希望更多的SQL實現有類似的東西! – rfusca 2010-07-22 04:51:26

+0

MySQL和SQLite中GROUP BY的寬容行爲類似。但結果可能是任意的。 SQL標準不支持這些功能。 – 2010-07-22 15:20:30

3

基本上你需要決定如何解決關係,並且你需要除datestamp之外的其他專欄(至少在給定的用戶中)是唯一的,所以它可以用作tiebreaker。如果沒有別的,你可以使用id主鍵列。

如果你正在使用PostgreSQL 8.4另一種解決方案是窗口函數:

WITH numbered_user_stats AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY datestamp DESC) AS RowNum 
    FROM user_stats) AS numbered_user_stats 
) SELECT u.user_id, u.datestamp, u.post_count, u.friends_count, u.favorites_count 
FROM numbered_user_stats AS u 
WHERE u.RowNum = 1; 
+0

我想這些id列畢竟必須有用;不過,我不確定這種使用是否計劃好。 – 2010-07-22 04:21:33

0

利用現有的基礎設施,你可以使用:

SELECT u.user_id, u.datestamp, 
     MAX(u.post_count)  AS post_count, 
     MAX(u.friends_count) AS friends_count, 
     MAX(u.favorites_count) AS favorites_count 
    FROM id_max_date AS m JOIN user_stats AS u 
    ON m.user_id = u.user_id AND m.date = u.datestamp 
GROUP BY u.user_id, u.datestamp; 

這給你一個單一的價值爲每個'不一定是唯一'的專欄。但是,這並不能絕對保證三個最大值都出現在同一行中(儘管他們至少有一個適中的機會 - 並且它們將全部來自給定日期創建的最後一個條目)。

對於此查詢,單獨在日期標記上的索引是沒有幫助的;用戶標識和日期標記索引可以顯着提高查詢速度 - 或者更準確地說,它可以加速生成id_max_date表的查詢。

顯然,你也可以寫id_max_date表達作爲一個子查詢的FROM子句:

SELECT u.user_id, u.datestamp, 
     MAX(u.post_count)  AS post_count, 
     MAX(u.friends_count) AS friends_count, 
     MAX(u.favorites_count) AS favorites_count 
    FROM (SELECT u2.user_id, MAX(u2.datestamp) AS date 
      FROM user_stats AS u2 
     GROUP BY u2.user_id) AS m 
    JOIN user_stats AS u ON m.user_id = u.user_id AND m.date = u.datestamp 
GROUP BY u.user_id, u.datestamp; 
相關問題