0

我有一个这样的查询

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 例子

4

2 回答 2

1

如果超过 ~ 5% 的表有val IS NOT NULL(很可能是这种情况),则索引对子句将val无用WHERE

不过,它可能对 JOIN 子句有所帮助,您可以在其中加入特定值。但是,更好的是:多列索引:

CREATE INDEX some_name_idx ON Location (location_id, val)

根据具体情况(特别是 if和val = 1arerare ,部分索引可能会更好地为您服务:val = 3

CREATE INDEX some_name1_idx ON Location (location_id)
WHERE val = 1

CREATE INDEX some_name3_idx ON Location (location_id)
WHERE val = 3

除此之外,如果使用的值不是常见的(小于表的 5%),那么您需要在 JOIN 或 WHERE 条件中使用的每一列上都有一个索引——通常情况下这是正确的。

所有通常的性能优化建议都适用。

如果您需要更具体的建议,则需要发布大量信息。阅读标签 wiki

由于您要加入这么多表,因此您可能会超出 Postgres 无法再找到最佳计划的一些限制(可能的变化太多)。首先编写最具选择性的JOIN可能变得很重要。在这种情况下,一般JOIN会出现在前面。在此处阅读手册中的更多信息。LEFT JOIN

CROSS JOIN通过代理

所以你有 10 个左连接。示例:如果其中一半有 3 个匹配项,则将行数乘以 3^5 = 243。或者如果它们全部有 5 个匹配项,则乘以 5^10 = 9765625。这必然会导致糟糕的性能。一切都是徒劳的,因为你只想要DISTINCT id最后。

锦上添花:要获得DISTINCT person.id,所有那些 LEFT JOIN 都是 100% 没用的。他们不会改变任何事情。只需将它们全部删除即可。

至于JOIN:替换那些EXISTS以避免成倍增加你的行。喜欢:

EXISTS (SELECT 1 FROM Func f1 WHERE f1.id = pf1.func_id)
于 2013-03-26T08:15:12.757 回答
0

WHERE 子句实际上似乎是多余的。您已经根据作为特定值的值进行了连接,然后您的 WHERE 子句验证这些值不是 NULL,因此所做的只是验证连接是否发生。因此,如果您只获取发生连接的行,则可以只使用 INNER JOIN 而不是 LEFT JOIN。==> f2.val 只有在没有发生 LEFT JOIN 时才会为空。

左连接位置 f2 ON f2.id = pf2.location_id AND f2.val='1'

其中f2.val不为空

因此,在这种情况下,您可能只尝试内部连接。

您还可以识别与您想要的值相对应的 ID,然后加入这些 ID。这些将是主键,并且可能更有效。

确定真正的问题

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

  1. 按原样运行查询。
  2. 删除第一个 where 子句表达式。
  3. 删除第二个 where 子句表达式。
  4. 删除一个联接
  5. 删除另一个联接...
于 2013-03-26T16:31:47.400 回答