这是展示三个有效答案的性能的测试。
EXISTS
优于带有LEFT JOIN
/的那个GROUP BY
:
测试设置
具有 100k 行的表,有 1000 个不同的值b
。
性能差距随着行数的增加而扩大 - 更少的重复意味着更少的差异。
没有索引。
CREATE TABLE tbl (a text, b text);
INSERT INTO tbl
SELECT (random()*10000)::int::text
,(random()*1000)::int || ' some more text here'
FROM generate_series(1, 100000) g;
1. @古法:LEFT JOIN
//GROUP BY
HAVING
EXPLAIN ANALYZE
SELECT t.a, t.b
FROM tbl t
LEFT join tbl t2 on t2.b = t.b and t2.a <> t.a
GROUP by t.a, t.b
HAVING count(t2.a) >= 1;
2. 相同的,解开的只是JOIN
/GROUP BY
EXPLAIN ANALYZE
SELECT t.a, t.b
FROM tbl t
JOIN tbl t2 ON t2.b = t.b AND t2.a <> t.a
GROUP BY t.a, t.b;
3. @布兰科:EXISTS
EXPLAIN ANALYZE
SELECT *
FROM tbl t
WHERE EXISTS (
SELECT *
FROM tbl t2
WHERE t2.a <> t.a
AND t2.b = t.b
);
4. @波西米亚:DISTINCT
EXPLAIN ANALYZE
SELECT DISTINCT t.a, t.b
FROM tbl t
JOIN tbl t2 on t2.b = t.b and t2.a <> t.a;
-> SQLfiddle显示查询的 EXPLAIN ANALYZE 输出。
- 总运行时间:12208.954 毫秒
- 总运行时间:11504.460 毫秒
- 总运行时间:272.508 毫秒——!~ 比 1 快 45 倍。
- 总运行时间:11540.627 毫秒
添加多列索引(SQLfiddle)后..
CREATE INDEX a_b_idx ON tbl(b, a);
..运行时不会改变。Postgres 不使用索引。它显然希望顺序表扫描更快,因为无论如何都必须读取整个表。
除了执行时间,还要注意行数,这证明了我所讨论
的观点: JOIN 创建了很多中间重复项,EXISTS
版本开始避免这些重复项:
输出EXPLAIN ANALYZE
为1.:
HashAggregate(成本=230601.26..230726.26 行=10000 宽度=31)(实际时间=12127.090..12183.087 行=99476 循环=1)
过滤器:(计数(t2.a)> = 1)
-> Hash Left Join (cost=3670.00..154661.89 rows=10125250 width=31) (实际时间=99.591..5897.744 rows=9991102 loops=1)
哈希条件:(tb = t2.b)
加入过滤器:(t2.a ta)
加入过滤器删除的行:101052
-> Seq Scan on tbl t (cost=0.00..1736.00 rows=100000 width=27) (实际时间=0.036..36.197 rows=100000 loops=1)
-> 哈希(成本=1736.00..1736.00 行=100000 宽度=27)(实际时间=99.141..99.141 行=100000 循环=1)
存储桶:2048 批次:8 内存使用量:784kB
-> tbl t2 上的 Seq Scan(成本=0.00..1736.00 行=100000 宽度=27)(实际时间=0.004..44.899 行=100000 循环=1)
总运行时间:12208.954 毫秒
3的输出EXPLAIN ANALYZE
。:
Hash Semi Join (cost=3670.00..7783.00 rows=1 width=27) (实际时间=81.630..247.371 rows=100000 loops=1)
哈希条件:(tb = t2.b)
加入过滤器:(t2.a ta)
加入过滤器删除的行数:1009
-> Seq Scan on tbl t (cost=0.00..1736.00 rows=100000 width=27) (实际时间=0.010..32.758 rows=100000 loops=1)
-> 哈希(成本=1736.00..1736.00 行=100000 宽度=27)(实际时间=81.388..81.388 行=100000 循环=1)
存储桶:2048 批次:8 内存使用量:784kB
-> tbl t2 上的 Seq Scan(成本=0.00..1736.00 行=100000 宽度=27)(实际时间=0.003..32.114 行=100000 循环=1)
总运行时间:272.508 毫秒