如何优化多个连接查询?

时间:2013-03-26 07:26:55

标签: sql postgresql query-optimization postgresql-performance

我有这样的查询

SELECT DISTINCT p.id 
FROM person p 
    INNER JOIN person_func pf1 
        ON p.id = pf1.person_id 
    INNER JOIN Func f1 
        ON f1.id = pf1.func_id
    LEFT JOIN person_location pf2 
        ON p.id = pf2.person_id 
    LEFT JOIN Location f2 
        ON f2.id = pf2.location_id AND f2.val='1'
    LEFT JOIN person_location pf3 
        ON p.id = pf3.person_id 
    LEFT JOIN Location f3 
        ON f3.id = pf3.location_id and f3.val='3'
WHERE f2.val IS NOT NULL OR f3.val IS NOT NULL;

并且共同有9-10个这样的连接。它运行得非常慢。我在person_func(person_id),person_location(person_id)上添加了索引,但它没有帮助。我该怎么做才能优化它?

示例 - SQLFiddle Example

2 个答案:

答案 0 :(得分:1)

如果超过约5%的表格有val IS NOT NULL(很可能就是这种情况),val上的索引无用 {{1}条款。

但是,在您加入特定值的情况下,它可能对JOIN子句有帮助。但是,甚至更好:多列索引:

WHERE

根据具体情况(特别是,如果CREATE INDEX some_name_idx ON Location (location_id, val) val = 1 罕见),部分索引可能会为您提供更好的服务:

val = 3

除此之外,您需要在JOIN或WHERE条件中使用的每个列上使用索引,如果使用的值不是通用位置(小于表的约5%) - 通常是真的。 / p>

所有the usual advice for performance optimization都适用。

如果您需要更具体的建议,您需要发布很多信息。阅读tag wiki

由于您加入了这么多表格,因此您可能会超越Postgres无法找到最佳计划的一些限制(太多可能的变化)。首先编写最具选择性的 JOIN可能很重要。通常CREATE INDEX some_name1_idx ON Location (location_id) WHERE val = 1 CREATE INDEX some_name3_idx ON Location (location_id) WHERE val = 3 在此案例中位于JOIN之前。 Read more in the manual here.

代理

LEFT JOIN

所以你有10个LEFT JOIN。示例:如果其中一半有3个匹配,则将行数乘以3 ^ 5 = 243.或者如果它们都有5个匹配,则乘以5 ^ 10 = 9765625.这必须导致可怕< / em>表现。而且一切都没有,因为你最终只想要CROSS JOIN

锦上添花:获得DISTINCT id,所有这些LEFT JOIN都是100%无用的。他们不会改变一件事。只需将它们全部删除。

至于DISTINCT person.id:将JOIN替换为EXISTS,以避免乘以您的行。像:

EXISTS (SELECT 1 FROM Func f1 WHERE f1.id = pf1.func_id)

答案 1 :(得分:0)

WHERE子句实际上似乎是多余的。您已经基于特定值的值加入,然后您的WHERE子句验证这些值是非NULL,所以这样做就是验证是否发生了连接。因此,如果您只获取连接发生的行,则可以使用INNER JOIN而不是LEFT JOIN。 ==&GT;如果没有发生LEFT JOIN,则f2.val将仅为空。

  

LEFT JOIN位置f2 ON f2.id = pf2.location_id AND f2.val ='1'

     

f2.val IS NOT NULL

所以,你可以在这种情况下尝试一个内连接。

您还可以识别与您想要的值对应的ID,然后加入这些ID。那些将成为主键,可能会更有效率。

识别真实问题

我还建议你逐个取出每个连接并重新运行查询,注意每次减少时间。这将有希望向您显示导致问题的join或where子句元素。

  1. 按原样运行查询。
  2. 删除第一个where子句表达式。
  3. 删除第二个where子句表达式。
  4. 删除一个联接
  5. 删除其他加入...