您的查询看起来已经不错了。使用普通的[INNER] JOIN
or LEFT [OUTER] JOIN
,就像 Gordon 建议的那样。但这不会有太大变化。
你提到那张桌子B
只有......
最多一百行
虽然桌子A
有...
数千行
如果每行有很多行created_by
(我希望如此),那么就有可能进行模拟索引跳过扫描。(在即将到来的 Postgres 版本之一中,
模拟它的需要可能会消失。)
基本成分是这个多列索引:
CREATE INDEX ON a (org_id, created_by);
它可以替换just 上的简单索引,(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%';
db<>fiddle here(Postgres 12,但也适用于 Postgres 9.6。)
这是子查询中的递归CTELATERAL
,使用相关子查询。
它利用上面的多列索引为每个(org_id, created_by)
. 如果表已足够真空,则使用仅索引扫描。
复杂 SQL的主要目标是完全避免对大表进行顺序扫描(甚至位图索引扫描),并且只读取很少的快速索引元组。
由于增加的开销,对于不利的数据分布(每行很多 org_id
和/或只有几行created_by
)可能会慢一些,但在有利的条件下它要快得多,并且可以很好地扩展,即使对于数百万行也是如此。您必须进行测试才能找到最佳位置。
有关的: