【发布时间】:2018-03-14 03:54:27
【问题描述】:
我有一个名为 sources 的表,其中包含价格,我有另一个名为 destinations 的表,其中包含另一组值。我需要获取每个源的所有目标,因此进行交叉连接,将源表中的每个值与目标表中的每个值相乘 source_id 和destination_id 是主键,我想将此结果表与另一个表内部连接,该表当前给我一个嵌套循环
方法 1
//has a nested loop
EXPLAIN SELECT * FROM
(select concat(s.source_id, ':', d.destination_id) AS pair_id,
(s.price * d.price) AS pair_price
FROM e1_sources s
CROSS JOIN e1_destinations d) AS p
INNER JOIN e1_alerts a
ON a.pair=p.pair_id
WHERE
(p.pair_price > a.value AND a.direction=true) OR
(p.pair_price <= a.value AND a.direction=false)
方法 2
//has a nested loop
EXPLAIN WITH pairs AS
(
SELECT
concat(s.source_id, ':', d.destination_id) AS pair_id,
(s.price * d.price) AS pair_price
FROM e1_sources s
CROSS JOIN e1_destinations d
)
SELECT * from pairs p
INNER JOIN e1_alerts a
ON p.pair_id=a.pair
WHERE
(p.pair_price > a.value AND a.direction=true) OR
(p.pair_price <= a.value AND a.direction=false)
方法 1 分析
"Hash Join (cost=3697.72..210978.26 rows=1297875 width=114)"
" Hash Cond: (concat(s.source_id, ':', d.destination_id) = (a.pair)::text)"
" Join Filter: ((((s.price * d.price) > a.value) AND a.direction) OR (((s.price * d.price) <= a.value) AND (NOT a.direction)))"
" -> Nested Loop (cost=0.00..19303.43 rows=1540440 width=70)"
" -> Seq Scan on e1_sources s (cost=0.00..25.56 rows=1556 width=16)"
" -> Materialize (cost=0.00..24.85 rows=990 width=54)"
" -> Seq Scan on e1_destinations d (cost=0.00..19.90 rows=990 width=54)"
" -> Hash (cost=2025.00..2025.00 rows=75098 width=50)"
" -> Seq Scan on e1_alerts a (cost=0.00..2025.00 rows=75098 width=50)"
" Filter: (direction OR (NOT direction))"
ARPPOACH 2 分析
"Hash Join (cost=56349.38..649740.92 rows=7089424 width=114)"
" Hash Cond: (p.pair_id = (a.pair)::text)"
" Join Filter: (((p.pair_price > a.value) AND a.direction) OR ((p.pair_price <= a.value) AND (NOT a.direction)))"
" CTE pairs"
" -> Nested Loop (cost=0.00..19378.74 rows=1104760 width=64)"
" -> Seq Scan on e1_sources s (cost=0.00..26.56 rows=1556 width=16)"
" -> Materialize (cost=0.00..20.65 rows=710 width=54)"
" -> Seq Scan on e1_destinations d (cost=0.00..17.10 rows=710 width=54)"
" -> CTE Scan on pairs p (cost=0.00..22095.20 rows=1104760 width=64)"
" -> Hash (cost=20248.06..20248.06 rows=751007 width=50)"
" -> Seq Scan on e1_alerts a (cost=0.00..20248.06 rows=751007 width=50)"
" Filter: (direction OR (NOT direction))"
但是,如果我有一个单独的表,其中包含作为 pair_id 的交叉联接产品,然后如果我进行了内部联接,我只需在分析中进行哈希扫描,查询几乎不需要几毫秒
方法 3 我有一个名为对的物化视图,其中包含源和目标的交叉连接,其连接的 pair_id 作为主键 现在内连接只需要几秒钟,因为它不执行嵌套循环
EXPLAIN ANALYZE
SELECT * from pairs p
INNER JOIN e1_alerts a
ON p.pair_id = a.pair
WHERE
(p.pair_price > a.value AND a.direction=true) OR
(p.pair_price <= a.value AND a.direction=false)
分析方法 3
"Hash Join (cost=1459.32..4892.41 rows=30566 width=73) (actual time=14.048..92.158 rows=498 loops=1)"
" Hash Cond: ((a.pair)::text = p.pair_id)"
" Join Filter: (((p.pair_price > a.value) AND a.direction) OR ((p.pair_price <= a.value) AND (NOT a.direction)))"
" Rows Removed by Join Filter: 99502"
" -> Seq Scan on e1_alerts a (cost=0.00..2025.00 rows=75098 width=50) (actual time=0.010..16.658 rows=100000 loops=1)"
" Filter: (direction OR (NOT direction))"
" -> Hash (cost=836.92..836.92 rows=49792 width=23) (actual time=13.736..13.736 rows=49792 loops=1)"
" Buckets: 65536 Batches: 1 Memory Usage: 3245kB"
" -> Seq Scan on pairs p (cost=0.00..836.92 rows=49792 width=23) (actual time=0.005..5.029 rows=49792 loops=1)"
"Planning time: 0.494 ms"
"Execution time: 92.262 ms"
几个问题
- 方法 1 和 2 是否因为不知道 pair_id 是否为主键而进行嵌套连接,有什么方法可以告诉 postgresql 由交叉连接产生的特定列是唯一的吗?
- 除了使用物化视图之外没有其他方法吗?我的源 x 目标表最多将包含 80000 个值,需要每 x 分钟更新一次,我不想向数据库发送这么多更新。如果我只发送大约 2000 个值的源和目标,我将能够从交叉连接生成对表
【问题讨论】:
-
注意:大多数人不喜欢水平滚动。 (我怀疑你是否真的需要子查询)
-
@wildplasser 这是“select concat(s.source_id, ':', d.destination_id) as pair_id”部分,因此我添加了一个子查询,如果您知道更好的方法来执行此操作,如果你能分享,超级有用,谢谢
-
看起来您使用 concat() 来模拟复合(主)键
concat(s.source_id, ':', d.destination_id) as pair_id,。为什么? -
字符串“XYZ:ABC”大致是连接列的样子,我在 e1_alerts 中有一个这样的列,我想与它执行内部连接
-
为什么
e1_alerts没有复合键(或索引)
标签: sql postgresql query-optimization postgresql-performance