计算来自多个表的连接数

Posted

技术标签:

【中文标题】计算来自多个表的连接数【英文标题】:Count Joins from Multiple Tables 【发布时间】:2020-09-28 01:02:57 【问题描述】:

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

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

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

message: pk_message_id, fk_user_id, fk_group_id, child_message_id, ...

group_prospective_userpk_prospective_user_id, fk_group_id, ...

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

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

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

对这些表的每个查询都具有相同的相关逻辑,用于检查当前用户所属的组。有没有有效的方法将多个类似的查询合并到一个查询中?

我尝试将多个LEFT JOINs 与select count distinct 一起使用,但这在包含大量消息和大量邀请的群组中遇到了性能问题。有没有办法通过子查询轻松/有效地做到这一点?

【问题讨论】:

快速提示:使用表别名并避免重复长的多部分表名。 @Parfait 谢谢!我实际上正在使用 JOOQ,所以这一切都是为我自动生成的,但我会记住它以备将来使用。另外,我想如果我在这里这样做会有助于提高可读性,现在我想到了。 我认为这看起来像带有所有双引号和多部分标识符的 ORM。由于 SQL 答案可能比用 JOOQ 编写的更复杂,因此请考虑提出 jooqjava 问题。 我的直接答案是通过组列将两者作为派生表连接起来。很难避免重复计算以合并来自不同数据源的汇总问题。 与您的问题无关,但是:Postgres 9.2 是no longer supported,您应该尽快计划升级。 【参考方案1】:

用户@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"```

【讨论】:

以上是关于计算来自多个表的连接数的主要内容,如果未能解决你的问题,请参考以下文章

同一张表上的多个连接,在一个查询中计数

如何查看oracle数据库某一张表的最大连接数

mysql中左连接后,最终的记录数大于左边表的记录分析

优化来自多个表的连接查询

表连接存数过程及基本编程

来自多个表的 Postgres/netezza 多重连接