Postgres,table1左连接table2,table1中每个ID只有1行

时间:2010-07-22 04:05:05

标签: sql postgresql greatest-n-per-group

好的,所以标题有点复杂。这基本上是每组最大的类型问题,但我不能为我的生活弄明白。

我有一个表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的统计信息。这是一个很大的表,在41m行的附近,所以我创建了一个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时,一些ID有多行:

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,但我总是听说那些效率非常低。想法?

3 个答案:

答案 0 :(得分:24)

DISTINCT ON是你的朋友。

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

答案 1 :(得分:3)

基本上你需要决定如何解决关系,除了datestamp之外还需要一些其他列,这些列保证是唯一的(至少在给定用户之上),因此它可以用作决胜局。如果不出意外,您可以使用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;

答案 2 :(得分: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和日期戳的索引可以大大加快此查询的速度 - 或者更准确地说,它可以加速生成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;