【问题标题】:Best way to get distinct count from a query joining two tables从连接两个表的查询中获取不同计数的最佳方法
【发布时间】:2020-07-16 15:00:14
【问题描述】:

我有 2 张桌子,桌子 A 和桌子 B

A(有数千行)

  • 身份证
  • uuid
  • 姓名
  • 类型
  • created_by
  • org_id

表格B(最多有一百行)

  • org_id
  • 组织名称

我正在尝试使用WHERE 子句获得最佳连接查询以获取计数。我需要表A 中不同created_bys 的计数,表B 中包含“myorg”的org_name。我目前有以下查询(产生预期结果),想知道是否可以进一步优化?

select count(distinct a.created_by)
from a left join
     b
     on a.org_id = b.org_id 
where b.org_name like '%myorg%';

【问题讨论】:

  • 请不要在给出答案后更改问题。而是开始一个新问题。我回滚了您的更改,您可以在revisions 中找到所有内容。
  • @ErwinBrandstetter,当然! new q

标签: sql postgresql join postgresql-performance postgres-9.6


【解决方案1】:

你不需要left join

select count(distinct a.created_by)
from a join
     b
     on a.org_id = b.org_id
where b.org_name like '%myorg%' 

对于这个查询,你需要一个关于b.org_id 的索引,我假设你有。

【讨论】:

    【解决方案2】:

    我会为此使用exists

    select count(distinct a.created_by)
    from a
    where exists (select 1 from b where b.org_id = a.org_id and b.org_name like '%myorg%')
    

    b(org_id) 上的索引会有所帮助。但在性能方面,关键点是:

    • 使用两边都带有通配符的like 进行搜索不利于性能(这不能利用索引);搜索完全匹配会好得多,或者至少在字符串的左侧没有通配符。

    • count(distinct ...) 比普通的count() 贵;如果您真的不需要distinct,那么就不要使用它。

    【讨论】:

    • like 应用在表 b 中,最多 100 行,因此在这种特殊情况下与性能几乎没有关系。在其他情况下,可以使用三元索引:stackoverflow.com/a/13452528/939860
    【解决方案3】:

    您的查询看起来已经不错了。像 Gordon 建议的那样,使用普通的 [INNER] JOINLEFT [OUTER] JOIN。但这不会有太大变化。

    你提到表B只有...

    最多一百行

    而表 A 有 ...

    数千行

    如果每个created_by 有很多行(我希望如此),那么emulated index skip scan 就有可能。
    (模仿它的需要可能会消失in one of the coming Postgres versions。)

    基本成分是这个多列索引

    CREATE INDEX ON a (org_id, created_by);
    

    它可以替换 (org_id) 上的简单索引,并且也适用于您的简单查询。见:

    您的情况有两个并发症:

    1. DISTINCT
    2. 0-n org_id 来自 org_name like '%myorg%'

    所以优化更难实现。但仍然可以使用一些花哨的 SQL:

    SELECT count(DISTINCT created_by)  -- does not count NULL (as desired)
    FROM   b
    CROSS  JOIN LATERAL (
       WITH RECURSIVE t AS (
          (  -- parentheses required
          SELECT created_by
          FROM   a
          WHERE  org_id = b.org_id
          ORDER  BY created_by
          LIMIT 1
          )
          UNION ALL
          SELECT (SELECT created_by
                  FROM   a
                  WHERE  org_id = b.org_id
                  AND    created_by > t.created_by
                  ORDER  BY created_by
                  LIMIT  1)
          FROM   t
          WHERE  t.created_by IS NOT NULL  -- stop recursion
          )
       TABLE t
       ) a
    WHERE  b.org_name LIKE '%myorg%';
    

    dbfiddle here(Postgres 12,但也适用于 Postgres 9.6。)

    这是LATERAL 子查询中的recursive CTE,使用相关子查询。

    它利用上面的多列索引只为每个(org_id, created_by) 检索一个 行。如果表已足够真空,则使用仅索引扫描。

    复杂 SQL 的主要目标是完全避免对大表进行顺序扫描(甚至位图索引扫描),并且只读取很少的快速索引元组。

    由于增加的开销,对于不利的数据分布(很多 org_id 和/或每个created_by 仅有几行行)可能会慢一些)但是在有利的条件下,它要快得多,并且可以很好地扩展,即使是数百万行也是如此。您必须进行测试才能找到最佳位置。

    相关:

    【讨论】:

      猜你喜欢
      • 2020-07-17
      • 1970-01-01
      • 1970-01-01
      • 2010-10-24
      • 1970-01-01
      • 1970-01-01
      • 2021-04-29
      • 2019-05-27
      • 1970-01-01
      相关资源
      最近更新 更多