Postgres选择顺序扫描而不是在多个INNER JOIN上进行更快的索引扫描

时间:2018-01-15 10:50:39

标签: postgresql postgres-9.4

我们有以下查询:

SELECT ...
FROM Client c
INNER JOIN Address a1 ON c.Address1 = a1.Id
INNER JOIN Address a2 ON c.Address2 = a2.Id
INNER JOIN Address a3 ON c.Address3 = a3.Id
WHERE c.Status = 0
AND a1.Status = 0
AND a2.Status = 0
AND a3.Status = 0
AND c.Id = 'specific id'

如您所见,我们使用不同的外键多次连接到同一个表。所有表格都有一个Status列,我们会为每个表格过滤Status = 0

使用EXPLAIN ANALYZE进行分析时,可以看到查询计划程序为所有表选择了索引扫描,之外的Address表之一( a2加入c.Address2。使用Status = 0的顺序扫描扫描此单个表,然后使用c.Address2 = a2.Id加入。

此策略比索引扫描成本高得多。我们只选择一个Client行,因此整个查询只返回一行,因此c.Address2 = a2.Id谓词比a2.Status = 0谓词更具限制性。

好的,所以我想也许这个外键有一些特殊的东西,这会导致统计信息误导查询优化器。所以我删除了a2的联接,这给我们留下了以下查询:

SELECT ...
FROM Client c
INNER JOIN Address a1 ON c.Address1 = a1.Id
INNER JOIN Address a3 ON c.Address3 = a3.Id
WHERE c.Status = 0
AND a1.Status = 0
AND a3.Status = 0
AND c.Id = 'specific id'

但现在查询优化器在a3上使用顺序扫描。仍使用索引扫描a1

我一直认为查询优化器使用表统计信息(本例中为Address)和限制查询(aX.Status = 0c.AddressX = aX.Id)来决定使用哪个计划。但在这里,它不止于此。当我将连接包含到a2时,它使用a2的顺序扫描和其余的索引扫描。当我将联接移至a2时,它会对a3使用顺序扫描。 因此,a2选择的策略取决于是否存在与a3无关的加入。

这种行为可能是什么原因?

请注意,统计数据是最新的,表格都已被抽真空。

0 个答案:

没有答案