Sql 如何优化多重连接查询?

Sql 如何优化多重连接查询?,sql,postgresql,query-optimization,postgresql-performance,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 L

我有一个这样的问题

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\u func(person\u id)和person\u location(person\u id)上添加了索引,但没有任何帮助。我可以做些什么来优化它


例如-

如果表中约5%以上的
val不为空
(很可能是这种情况),则
val
上的索引对于
WHERE
子句将无效

不过,它可能对JOIN子句有所帮助,因为您可以对特定的值进行连接。但是,更好的是:多列索引:

CREATE INDEX some_name_idx ON Location (location_id, val)
根据具体情况(特别是如果
val=1
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%),则在联接或WHERE条件中使用的每个列上都需要一个索引,这通常是正确的

一切都适用

如果你需要更具体的建议,你需要发布很多mor信息。读读这本书

因为你加入了这么多的表格,你可能会超过一些限制,Postgres再也找不到最好的计划(太多可能的变化)。首先编写最有选择性的连接可能变得很重要。在这种情况下,
JOIN
通常位于
LEFT JOIN
之前

通过代理交叉连接
所以你有10个左连接。示例:如果其中一半有3个匹配项,则将行数乘以3^5=243。或者,如果它们都有5个匹配项,则乘以5^10=9765625。这将导致糟糕的性能。这一切都是徒劳的,因为您最终只需要
不同的id

锦上添花:为了获得
独特的person.id
,所有这些左连接都是100%无用的。他们什么也没变。把它们都拿走

至于
JOIN
:将那些替换为,以避免增加行数。比如:

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

WHERE子句实际上似乎是多余的。您已经基于作为特定值的值进行了连接,然后WHERE子句验证这些值不是NULL,因此所做的只是验证连接是否发生。因此,如果只获取发生连接的行,则可以使用内部连接而不是左连接。==>f2.val仅在未发生左连接时才为null

f2.id=pf2.Location_id和f2.val='1'

其中f2.val不为空

所以,在这种情况下,您可以尝试内部联接

您还可以标识与所需值相对应的ID,并将它们连接起来。这些将是主键,效率可能会更高

确定真正的问题

我还建议您一个接一个地删除每个join,然后重新运行查询,并注意每次减少的时间。这将有希望向您显示是哪个join或where子句元素导致了问题

  • 按原样运行查询
  • 删除第一个where子句表达式
  • 删除第二个where子句表达式
  • 删除一个连接
  • 删除另一个连接

  • 你是对的!我已经更新了一个url-9.1If columns
    val
    只能由数值组成,将列数据类型更改为INT或SMALLINT,并用索引覆盖这些列,因为WHERE子句通常由索引覆盖。此外,您还需要为连接中出现的所有外键创建索引(PERSON(func_id)、PERSON(location_id))不,它包含varchar(20)数据,这只是为了简单起见OK,不管怎样,您需要用索引覆盖它,因为它出现在WHERE子句中确切的是
    count(p.id)
    应该计数?位置等关节表非常小-10-11个条目。这些是person的属性(例如person通过一对多连接,我查询person时必须有一些条件(我通过内部连接连接),并且很高兴有一些条件(我使用左连接连接,并将一个独立于p.Id)。然后我只是使用行计算分数(NICE To have中非空的数量)@zobber:对于包含10个条目的小表,索引是完全无用的。