计数来自多个表的连接

Count Joins from Multiple Tables

作为参考,我使用的是 Postgres 9.2.23。

我有几个 table,其中一个 table (user_group) 与其他一些 table 相关(例如:postsgroup_invites,还有一些其他的)。还有一个 groups table,但它不包含我进行这些查询所需的任何数据。

Tableuser_group: fk_user_group_id, fk_user_id, fk_group_id, fk_invite_id user_status, ...

Tablemessage: pk_message_id, fk_user_id, fk_group_id, child_message_id, ...

Tablegroup_prospective_user: pk_prospective_user_id, fk_group_id, ...

我想为指定组 ID 列表的每个相关 table 获取一些统计信息 if该用户是该组的成员。

现在我对每个相关 table 进行一个查询,例如:

select 
  "public"."user_group"."fk_group_id" as "groupId", 
  count(case
    when (
      "public"."message"."child_message_id" is null
      and "public"."message"."pk_message_id" is not null
    ) then "public"."message"."pk_message_id"
  end) as "numDiscussions", 
  count("public"."message"."pk_message_id") as "numDiscussionPosts"
from "public"."user_group"
  left outer join "public"."message"
    on "public"."message"."fk_group_id" = "public"."user_group"."fk_group_id"
where (
  "public"."user_group"."fk_group_id" in (
    1, 11, 23, 530, 1070
  )
  and "public"."user_group"."role" in (
    'ADMINISTRATOR', 'MODERATOR', 'MEMBER'
  )
  and "public"."user_group"."fk_user_id" = 17517
)
group by "public"."user_group"."fk_group_id"

以及邀请:

select 
  "public"."user_group"."fk_group_id" as "groupId", 
  count(case
    when "public"."prospective_user"."status" = 1 then "public"."prospective_user"."pk_prospective_user_id"
  end) as "numInviteesExternal"
from "public"."user_group"
  left outer join "public"."prospective_user"
    on "public"."prospective_user"."fk_group_id" = "public"."user_group"."fk_group_id"
where (
  "public"."user_group"."fk_group_id" in (
    1, 11, 23, 530, 6176
  )
  and "public"."user_group"."role" in (
    'ADMINISTRATOR', 'MODERATOR', 'MEMBER'
  )
  and "public"."user_group"."fk_user_id" = 17517
)
group by "public"."user_group"."fk_group_id"

统计群组邀请数的查询与上述查询非常相似。只是 count whenjoin on 发生了变化。

对这些 table 的每个查询都具有相同的相关逻辑,用于检查当前用户是活跃成员的组。有没有有效的方法将多个类似的查询合并为一个查询?

我尝试将多个 LEFT JOINselect count distinct 一起使用,但是 运行 在具有大量消息和大量邀请的群组中出现性能问题。有没有办法 easily/efficiently 使用子查询来做到这一点?

用户@Parfait 的回答是我能找到的最具扩展性的解决方案。我的查询基于本教程:https://www.sqlteam.com/articles/using-derived-tables-to-calculate-aggregate-values.

虽然这并不完美,并且会导致一堆子查询 运行,但它确实一次获取了所有数据,而且只需要访问数据库一次。

结果是这样的:

  "groups"."groupId", 
  coalesce(
    "members"."member_count", 
    0
  ) as "numActiveMembers", 
  coalesce(
    "members"."invitee_count", 
    0
  ) as "numInviteesInternal", 
  coalesce(
    "discussions"."discussions_count", 
    0
  ) as "numDiscussions", 
  coalesce(
    "discussions"."posts_count", 
    0
  ) as "numDiscussionPosts"
from (
  select "public"."user_group"."fk_group_id" as "groupId"
  from "public"."user_group"
  where (
    "public"."user_group"."fk_group_id" in (
      1, 2, 3, 4, 5
    )
    and "public"."user_group"."role" = 'ADMINISTRATOR'
    and "public"."user_group"."fk_user_id" = 123
  )
  group by "public"."user_group"."fk_group_id"
) as "groups"
  left outer join (
    select 
      "public"."user_group"."fk_group_id" as "members_group_id", 
      count(distinct case
        when "public"."user_group"."role" in (
          'ADMINISTRATOR', 'MODERATOR', 'MEMBER'
        ) then "public"."user_group"."pk_user_group_id"
      end) as "member_count", 
      count(distinct case
        when "public"."user_group"."role" = 'INVITEE' then "public"."user_group"."pk_user_group_id"
      end) as "invitee_count"
    from "public"."user_group"
    group by "public"."user_group"."fk_group_id"
  ) as "members"
    on "members_group_id" = "groupId"
  left outer join (
    select 
      "public"."message"."fk_group_id" as "discussions_group_id", 
      count(case
        when (
          "public"."message"."child_message_id" is null
          and "public"."message"."pk_message_id" is not null
        ) then "public"."message"."pk_message_id"
      end) as "discussions_count", 
      count("public"."message"."pk_message_id") as "posts_count"
    from "public"."message"
    group by "public"."message"."fk_group_id"
  ) as "discussions"
    on "discussions_group_id" = "groupId"```