如何简化这个复杂的查询?

时间:2019-09-24 03:52:25

标签: postgresql

所引用的表定义为:

CREATE TABLE message
(
    id BIGINT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    guild_id BIGINT NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP NOT NULL,
);


CREATE TABLE d_user
(
    id BIGINT PRIMARY KEY
);


CREATE TABLE vcsession
(
    id BIGINT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    guild_id BIGINT NOT NULL,
    duration INTEGER NOT NULL,
    began_at TIMESTAMP NOT NULL,
    last_active TIMESTAMP NOT NULL
);

此查询的预期结果集应该由行中的每个用户行组成,并为以下内容提供列:

  • user_id:用户ID
  • message_count:每个用户在两个日期时间定义的间隔内发送的消息数(如果没有发送消息,则为0)
  • voice_time:在两个日期时间定义的时间间隔内,每个语音会话的最后一次持续时间之和(如果没有语音会话处于活动状态,则应为0)
  • active_days:用户发送消息或进行有效语音会话的天数(如果用户在提供的时间间隔内未处于活动状态,则应为0)

这是我写的查询:

select
    activity.user_id,
    message_count,
    voice_time,
    coalesce(active_days, 0) as active_days
from (
    select
         d_user.id as user_id,
         coalesce(messages.count, 0) as message_count,
         coalesce(vcsessions.duration, 0) as voice_time
    from d_user left join (
        select
            user_id,
            count(*) as "count"
        from message where (
            (guild_id = $1) and
            (created_at >= $2) and
            (created_at < $3)
        ) group by user_id
    ) as messages on messages.user_id = d_user.id left join (
        select
            user_id,
            sum(duration) as  "duration"
        from vcsession where (
            (guild_id = $1) and
            (last_active >= $2) and
            (last_active < $3)
        ) group by user_id
    ) as vcsessions on vcsessions.user_id = d_user.id
) as activity left join (
    select user_id, count(*) as active_days from (
        select * from (
            select
                user_id,
                (cast(extract(EPOCH from message.created_at) as int) - cast(extract(EPOCH from $2) as int)) / 86400 as day_offset
            from message where (
                (created_at >= $2) and
                (created_at < $3)
            ) group by user_id, day_offset
        ) as message_days union (
            select
                user_id,
                (cast(extract(EPOCH from vcsession.last_active) as int) - cast(extract(EPOCH from $2) as int)) / 86400 as day_offset
            from vcsession where (
                (last_active >= $2) and
                (last_active < $3)
            ) group by user_id, day_offset
        )
    ) as active_days group by user_id
) as active_days on active_days.user_id = activity.user_id

这就是结果集的样子:

|user_id             |message_count       |voice_time          |active_days         |
|--------------------|--------------------|--------------------|--------------------|
|1                   |752                 |694                 |1                   |
|2                   |12                  |543                 |2                   |
|3                   |323                 |7163                |4                   |
|4                   |56                  |870                 |3                   |

1 个答案:

答案 0 :(得分:2)

对我来说,它看起来可读性强。

也许您可以将forst FROM子句中的两个子选择拉入主查询:

SELECT ...
FROM ((SELECT ...) AS messages
      LEFT JOIN
      (SELECT ...) AS vcsessions
     ) AS ...
   LEFT JOIN ...

有可能成为

SELECT ...
FROM (SELECT ...) AS messages
   LEFT JOIN
   (SELECT ...) AS vcsessions
   LEFT JOIN ...