带嵌套sql子查询的postgres性能问题

带嵌套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都有索引 有没有办法

为了简化这个案例,我们假设有以下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操作员的案例:

    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')
    )
    
    这将为您提供链接到C记录的所有A记录,其中C和b值是您提到的集合之一。希望这有帮助:)

    编辑似乎是多对一:

    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
    一起使用。有效地,使用显式临时表一次一组手动执行
    INTERSECT
    。根据子查询返回的行数,将索引添加到临时表中可能是有益的

    更新

    至于您的问题,为什么当查询变得复杂时Postgres的性能会下降。。。你的博士后版本相当陈旧,不太可能有人有足够的兴趣来详细调查。我只能提供一些一般性的想法。最新版本很可能会有不同的表现,自8.2以来有很多变化

    在每个RDBMS中,查询优化器分析查询的资源和时间都是有限的,因此他们使用了大量的启发式方法。由于查询中的连接数增加了问题的复杂性,找到最佳执行计划的难度呈指数级增加,因此必须有一个阈值,在该阈值之后,优化器将放弃并选择他得到的任何计划

    你应该能够观察到它。检查快速查询的执行计划,添加另一个联接以降低查询速度并比较计划。很可能这些计划会大不相同。您应该能够确定优化器在每种情况下选择的路径

    这可能是因为,当给定一个包含少量连接的查询时,优化器能够将其转换为一个变量,相当于使用
    intersect
    ,但如果连接数量很大,它就不能再执行此操作,而只能按照查询流一个接一个地执行连接。它甚至可能做得非常低效,以至于它在循环中循环,在循环中循环…,换句话说,复杂性从线性跳到二次或更糟

    因此,事实上,对这些性能问题的唯一答案是:检查执行计划

    顺便说一句,最新版本的Postgres有
    ,这有效地创建了一个带有中间结果的临时表。这对您的情况应该会有很大帮助,因为您的每个子查询都很简单,如果系统首先单独运行所有子查询,那么将结果组合在一起就很容易了

  • 升级到最新版本
  • 为清晰起见,请使用
    JOIN
    语法
  • 使用
    EXISTS(…)
    代替(…)
  • 中的
    ,以提高速度和舒适度
  • PK/FK和索引确实有帮助

    更简短:
    其中('1','2',('3','4'),('5','6'))中的(b.val_b,c.val_c)
    我实际上只需要满足所有标准的a_id。你提出的解决方案会发现记录不符合任何标准。那么必须是多对一关系,所以我们必须更聪明一点。让我在多对一解决方案中进行编辑,因为我受字符限制。值实际上是字符串。我在本例中只使用了数值,但可以是任何长度不超过255个字符的字符串。8.2?真正地8.x是(=支持的)。你应该尽快升级到9.4。弗拉基米尔,谢谢你的反馈。我的不支持,但根据你的建议,我内联了选择。因此,对于3个集合,它的性能肯定比原始版本好很多倍,但当我尝试7-8个集合时,它又花了大约4分钟来执行sql。我想知道我现在是否正在从\u collapse\u limit点击并加入\u collapse\u limit postgres默认配置值?@user1039384,我的第一个答案不正确。这与您的查询不同。如果
    a\u id
    C
    中被复制,它将产生重复的行。我的带有
    INTERSECT
    的第二个变量应该是正确的。请试一试。@user1039384,你最终选择做什么?如果此处提供的任何答案有用,请对其进行投票并接受最有用的答案。@user1039384,我关于查询复杂性如何影响性能的补充说明是否令人满意?您是否有机会检查您的执行计划并注意到它们是如何变化的
    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))