带嵌套sql子查询的postgres性能问题
为了简化这个案例,我们假设有以下3个表 A(A_id),B(B_id,val_B),C(A_id,B_id,val_C) 我需要从B和C中找到所有具有特定值对的a_id。例如,找到所有具有记录(val_B='1'和val_C='2'和B.B_id=C.B_id)和(val_B='3'和val_C='4'和B.B_id=C.B_id)的a_id,然后 我注意到,通过添加更多的对(val_b,val_c),postgres需要大量的时间来执行查询。注意ID、val_b和val_c都有索引 有没有办法优化查询?尝试了显式内部联接,但无助于提高性能 提前谢谢 更多信息:带嵌套sql子查询的postgres性能问题,sql,postgresql,database-performance,in-subquery,Sql,Postgresql,Database Performance,In Subquery,为了简化这个案例,我们假设有以下3个表 A(A_id),B(B_id,val_B),C(A_id,B_id,val_C) 我需要从B和C中找到所有具有特定值对的a_id。例如,找到所有具有记录(val_B='1'和val_C='2'和B.B_id=C.B_id)和(val_B='3'和val_C='4'和B.B_id=C.B_id)的a_id,然后 我注意到,通过添加更多的对(val_b,val_c),postgres需要大量的时间来执行查询。注意ID、val_b和val_c都有索引 有没有办法
- postgres版本8.2.4
- 只有一对标准运行时间为77.621ms
- 具有2对标准-151.588 ms
- 使用3对条件-49483.979 ms时,每个子查询必须再次命中索引,这将使查询的开销增加数倍。如果我理解您的要求,这是Or操作员的案例:
这将为您提供链接到C记录的所有A记录,其中C和b值是您提到的集合之一。希望这有帮助:) 编辑似乎是多对一:select a.a_id from A join c on a.a_id = c.a_id join b on b.b_id = c.b_id where ( (b.val_b = '1' and c.val_c = '2') or (b.val_b = '3' and c.val_c = '4') or (b.val_b = '5' and c.val_c = '6') )
Select a.a_id , sum(case when b.val_b = '1' and c.val_c = '2' then 1 else 0 end) as Condition1 , Sum(case when b.val_b = '3' and c.val_c = '4' then 1 else 0 end) as Condition2 , Sum(case when b.val_b = '5' and c.val_c = '6' then 1 else 0 end) as Condition3 from A join c on a.a_id = c.a_id join b on b.b_id = c.b_id group by a.a_id having sum(case when b.val_b = '1' and c.val_c = '2' then 1 else 0 end) > 0 and Sum(case when b.val_b = '3' and c.val_c = '4' then 1 else 0 end) > 0 and Sum(case when b.val_b = '5' and c.val_c = '6' then 1 else 0 end) > 0
希望这能让你达到目的,看看以下各项是否运行得更快会很有趣:
实际上,这里不是中的多个SELECT A.a_id FROM A WHERE A.a_id IN ( SELECT C.a_id FROM B INNER JOIN C ON B.b_id=C.b_id WHERE B.val_b='1' and C.val_c='2' INTERSECT SELECT C.a_id FROM B INNER JOIN C ON B.b_id=C.b_id WHERE B.val_b='3' and C.val_c='4' INTERSECT SELECT C.a_id FROM B INNER JOIN C ON B.b_id=C.b_id WHERE B.val_b='5' and C.val_c='6' ) ;
,而是多个子集的显式交集 我最初的回答中有一个问题与问题的最初问题并不相同 下面是一些示例数据和原始查询,以检查我的变体是否生成与原始查询相同的结果 编辑 还有一条路要调查。如果每个子查询都运行得很快,但是在一个长查询中重复多次的
变得非常慢,那么您可以尝试用子查询的结果填充一个临时表,然后将此临时表与主表INTERSECT
一起使用。有效地,使用显式临时表一次一组手动执行a
。根据子查询返回的行数,将索引添加到临时表中可能是有益的 更新 至于您的问题,为什么当查询变得复杂时Postgres的性能会下降。。。你的博士后版本相当陈旧,不太可能有人有足够的兴趣来详细调查。我只能提供一些一般性的想法。最新版本很可能会有不同的表现,自8.2以来有很多变化 在每个RDBMS中,查询优化器分析查询的资源和时间都是有限的,因此他们使用了大量的启发式方法。由于查询中的连接数增加了问题的复杂性,找到最佳执行计划的难度呈指数级增加,因此必须有一个阈值,在该阈值之后,优化器将放弃并选择他得到的任何计划 你应该能够观察到它。检查快速查询的执行计划,添加另一个联接以降低查询速度并比较计划。很可能这些计划会大不相同。您应该能够确定优化器在每种情况下选择的路径 这可能是因为,当给定一个包含少量连接的查询时,优化器能够将其转换为一个变量,相当于使用INTERSECT
,但如果连接数量很大,它就不能再执行此操作,而只能按照查询流一个接一个地执行连接。它甚至可能做得非常低效,以至于它在循环中循环,在循环中循环…,换句话说,复杂性从线性跳到二次或更糟 因此,事实上,对这些性能问题的唯一答案是:检查执行计划 顺便说一句,最新版本的Postgres有intersect
和
中的,这有效地创建了一个带有中间结果的临时表。这对您的情况应该会有很大帮助,因为您的每个子查询都很简单,如果系统首先单独运行所有子查询,那么将结果组合在一起就很容易了
- 升级到最新版本
- 为清晰起见,请使用
语法JOIN
- 使用
代替(…)EXISTS(…)
,以提高速度和舒适度
- PK/FK和索引确实有帮助李>
更简短:
我实际上只需要满足所有标准的a_id。你提出的解决方案会发现记录不符合任何标准。那么必须是多对一关系,所以我们必须更聪明一点。让我在多对一解决方案中进行编辑,因为我受字符限制。值实际上是字符串。我在本例中只使用了数值,但可以是任何长度不超过255个字符的字符串。8.2?真正地8.x是(=支持的)。你应该尽快升级到9.4。弗拉基米尔,谢谢你的反馈。我的不支持,但根据你的建议,我内联了选择。因此,对于3个集合,它的性能肯定比原始版本好很多倍,但当我尝试7-8个集合时,它又花了大约4分钟来执行sql。我想知道我现在是否正在从\u collapse\u limit点击并加入\u collapse\u limit postgres默认配置值?@user1039384,我的第一个答案不正确。这与您的查询不同。如果其中('1','2',('3','4'),('5','6'))中的(b.val_b,c.val_c)
在a\u id
中被复制,它将产生重复的行。我的带有C
的第二个变量应该是正确的。请试一试。@user1039384,你最终选择做什么?如果此处提供的任何答案有用,请对其进行投票并接受最有用的答案。@user1039384,我关于查询复杂性如何影响性能的补充说明是否令人满意?您是否有机会检查您的执行计划并注意到它们是如何变化的INTERSECT
Select a.a_id , sum(case when b.val_b = '1' and c.val_c = '2' then 1 else 0 end) as Condition1 , Sum(case when b.val_b = '3' and c.val_c = '4' then 1 else 0 end) as Condition2 , Sum(case when b.val_b = '5' and c.val_c = '6' then 1 else 0 end) as Condition3 from A join c on a.a_id = c.a_id join b on b.b_id = c.b_id group by a.a_id having sum(case when b.val_b = '1' and c.val_c = '2' then 1 else 0 end) > 0 and Sum(case when b.val_b = '3' and c.val_c = '4' then 1 else 0 end) > 0 and Sum(case when b.val_b = '5' and c.val_c = '6' then 1 else 0 end) > 0
SELECT A.a_id FROM A WHERE A.a_id IN ( SELECT C.a_id FROM B INNER JOIN C ON B.b_id=C.b_id WHERE B.val_b='1' and C.val_c='2' INTERSECT SELECT C.a_id FROM B INNER JOIN C ON B.b_id=C.b_id WHERE B.val_b='3' and C.val_c='4' INTERSECT SELECT C.a_id FROM B INNER JOIN C ON B.b_id=C.b_id WHERE B.val_b='5' and C.val_c='6' ) ;
SELECT A.a_id FROM A WHERE EXISTS ( SELECT * FROM B JOIN C ON B.b_id = C.b_id AND B.val_b = '1' WHERE C.a_id = A.a_id AND C.val_c = '2' ) AND EXISTS ( SELECT * FROM B JOIN C ON B.b_id = C.b_id AND B.val_b = '3' WHERE C.a_id = A.a_id AND C.val_c = '4' ) AND EXISTS ( SELECT * FROM B JOIN C ON B.b_id = C.b_id AND B.val_b = '5' WHERE C.a_id = A.a_id AND C.val_c = '6' ) ;
select a_id from a inner join c using (a_id) inner join b using (b_id) group by a_id having bool_or((val_b, val_c) = (1,2)) and bool_or((val_b, val_c) = (3,4)) and bool_or((val_b, val_c) = (5,6))