【问题标题】:How to join multiple times on the same table with the max value for each row如何在同一个表上多次加入每行的最大值
【发布时间】:2021-07-28 16:50:57
【问题描述】:

我有一个包含 5 个字段的表格活动:

id - numeric
user_id - numeric 
type_id - numeric
content_id - numeric
date_of_activity date

我需要做一个查询,其结果将是 7 列:

id(type1 activity),content_id(type 1) at max(date_of_activity for type 1),max(date_of_activity for type 1)content_id(type 2) at max(date_of_activity for type 2),max(date_of_activity for type 2)content_id(type 3) at max(date_of_activity for type 3),max(date_of_activity for type 3)

如果用户的 type_id 没有对应的值,则对应列中应显示 NULL 值。

例子:

users
    user_id: 1
    user_id: 2
    user_id: 3
activities

    id: 1
    user_id: 1
    type_id: 2
    date_of_activity: 2021/05/01
    content_id: 12

    id: 2
    user_id: 1
    type_id: 3
    date_of_activity: 2021/05/01
    content_id: 102

    id: 3
    user_id: 2
    type_id: 3
    date_of_activity: 2021/05/01
    content_id: 213

    id: 4
    user_id: 1
    type_id: 2
    date_of_activity: 2021/05/02
    content_id: 13

    id: 5
    user_id: 1
    type_id: 3
    date_of_activity: 2021/05/02
    content_id: 103

    id: 6
    user_id: 1
    type_id: 1
    date_of_activity: 2021/05/03
    content_id: 1

    id: 7
    user_id: 2
    type_id: 1
    date_of_activity: 2021/05/03
    content_id: 212
    
    id: 8
    user_id: 1
    type_id: 3
    date_of_activity: 2021/05/03
    content_id: 104

    id: 8
    user_id: 1
    type_id: 3
    date_of_activity: 2021/05/04
    content_id: 105

    id: 10
    user_id: 1
    type_id: 1
    date_of_activity: 2021/05/05
    content_id: 2

    id: 11
    user_id: 1
    type_id: 3
    date_of_activity: 2021/05/05
    content_id: 106

结果应该是:

id:10,
user_id:1,
type_id:1,
date_of_activity: 2021/05/05,
activity2.date_of_activity:2021/05/02,
activity2.content_id:13,
activity3.date_of_activity: 2021/05/05,
activity3.content_id:106

id:7,
user_id: 2,
type_id: 1,
date_of_activity: 2021/05/03,
content_id: 212,
activity2.date_of_activity: NULL,
activity2.content_id: NULL,
activity3.date_of_activity:2021/05/01
activity3.content_id:213

如您所见,我检索了一个活动,每个用户的每个活动类型,每个活动都是最新的,等等所有用户...

我有一个查询,可以检索每个用户的 ONE 活动类型的最新活动:

SELECT
activities.*,
dDates.max
FROM activities
JOIN users on users.id = activities.user_id
INNER JOIN (SELECT user_id,max(activities.date_of_activity) FROM activities WHERE activities.type_id = 6 AND activities.deleted_at IS NULL group by user_id) as dDates on activities.user_id = maxDates.user_id
WHERE activities.type_id = 6 AND activities.deleted_at IS NULL AND activities.date_of_activity = dDates.max;

但有时我需要将多个活动作为同一个查询的一部分加入,所以我试图一次获取所有内容,但我找不到一种不是很慢的方法。

我尝试为每种类型创建一个视图/使用“With”:

CREATE VIEW MostRecentType1 AS
SELECT
activities.*,
dDates.max
FROM activities
JOIN users on users.id = activities.user_id
INNER JOIN (SELECT user_id,max(activities.date_of_activity) FROM activities WHERE activities.type_id = 1 AND activities.deleted_at IS NULL group by user_id) as dDates on activities.user_id = maxDates.user_id
WHERE activities.type_id = 1 AND activities.deleted_at IS NULL AND activities.date_of_activity = dDates.max;
CREATE VIEW MostRecentType2 AS
SELECT
activities.*,
dDates.max
FROM activities
JOIN users on users.id = activities.user_id
INNER JOIN (SELECT user_id,max(activities.date_of_activity) FROM activities WHERE activities.type_id = 1 AND activities.deleted_at IS NULL group by user_id) as dDates on activities.user_id = maxDates.user_id
WHERE activities.type_id = 1 AND activities.deleted_at IS NULL AND activities.date_of_activity = dDates.max;

最后是视图的左连接

SELECT
activities.*,
dDates.max,
type2.date_of_activity as "type2.date_of_activity as",
type2.content_id as "type2.content_id",
type3.date_of_activity as "type3.date_of_activity" ,
type3.content_id as "type3.content_id" ,
FROM activities
JOIN users on users.id = activities.user_id
INNER JOIN (SELECT user_id,max(activities.date_of_activity) FROM activities WHERE activities.type_id = 1 AND activities.deleted_at IS NULL group by user_id) as dDates on activities.user_id = maxDates.user_id
LEFT JOIN MostRecentType2 as type2 on activities.user_id = type2.user_id
LEFT JOIN MostRecentType3 as type3 on activities.user_id = type3.user_id
WHERE activities.type_id = 1 AND activities.deleted_at IS NULL AND activities.date_of_activity = dDates.max;

但这根本不能很好地扩展。

在我大部分为空的活动表上,如果我只执行第一个查询,它将需要 4 毫秒,但是一旦我添加第一个“MostRecentType2”加入它就会跳转 到 40 毫秒,然后 MostRecentType3 到 80 毫秒并继续以这种方式递增,在某些情况下我需要大量的连接。

关于如何(更好地)进行查询的任何想法?

【问题讨论】:

    标签: sql postgresql join query-optimization


    【解决方案1】:

    嗯。 . .您可以使用DISTINCT ON 获取每个用户的每种类型的最新行。然后只需使用条件聚合:

    SELECT user_id,
           MAX(date_of_activity) FILTER (WHERE type_id = 1) as date_1,
           MAX(content_id) FILTER (WHERE type_id = 1) as content_1,
           MAX(date_of_activity) FILTER (WHERE type_id = 2) as date_2,
           MAX(content_id) FILTER (WHERE type_id = 2) as content_2,
           MAX(date_of_activity) FILTER (WHERE type_id = 3) as date_3,
           MAX(content_id) FILTER (WHERE type_id = 3) as content_3
    FROM (SELECT DISTINCT ON (user_id, type_id) a.*
          FROM activities a
          WHERE a.deleted_at IS NULL
          ORDER BY user_id, type_id, date_of_activity DESC
         ) a
    GROUP BY user_id
    

    【讨论】:

    • @joinjohn 。 . .您是否有理由不接受此答案?
    • 这个查询的唯一问题是我每次都给我整个活动集,而不是搜索的类型。在我的示例中,总是有一个“主要”活动,然后将该活动加入到其余活动中。 " - 我的示例的输出是最大日期的“1”类型活动列表,然后我进行加入
    • 是的,我很快就和我谈过了,我刚刚遇到并遇到了问题:(
    • @joinjohn 。 . .您可以添加where 子句来过滤您关心的活动。该过滤似乎不是您提出的问题的重要部分。
    • 我已经尝试过 where 子句,但它使查询的其余部分不起作用,因为它会在计算最大值之前过滤所有活动。如果我这样做 WHERE a.type_id = 6 content_2 , content_3 , date_2 , date_3 等的数据将始终为 NULL 不像我的例子
    猜你喜欢
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 2015-11-06
    • 2016-06-24
    • 2021-04-01
    • 1970-01-01
    • 2021-11-14
    • 1970-01-01
    相关资源
    最近更新 更多