2010-07-22 65 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; 
相关问题