我们有以下查询:
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 = 0
和c.AddressX = aX.Id
)来决定使用哪个计划。但在这里,它不止于此。当我将连接包含到a2
时,它使用a2
的顺序扫描和其余的索引扫描。当我将联接移至a2
时,它会对a3
使用顺序扫描。 因此,a2
选择的策略取决于是否存在与a3
无关的加入。
这种行为可能是什么原因?
请注意,统计数据是最新的,表格都已被抽真空。