您的查询看起来已经不错了。像 Gordon 建议的那样,使用普通的 [INNER] JOIN 或 LEFT [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) 上的简单索引,并且也适用于您的简单查询。见:
您的情况有两个并发症:
DISTINCT
- 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 仅有几行行)可能会慢一些)但是在有利的条件下,它要快得多,并且可以很好地扩展,即使是数百万行也是如此。您必须进行测试才能找到最佳位置。
相关: