我最近注意到包含CTE的性能较差的查询。
从运行EXPLAIN
开始,如果我在CTE中有2个连续的表,第一个应用了WHERE
过滤器,则postgres优化器实际上不会限制行设置,并且所以第二个表查找非常慢:
WITH thing_data AS (
SELECT * FROM things WHERE id = '0000000001'
), thing_readings AS (
SELECT thing_timestamp
FROM reading_log_instantaneous_schedule
INNER JOIN thing_data
ON thing_id = thing_data.id
ORDER BY thing_timestamp DESC LIMIT 1
),
SELECT thing_data.*
FROM thing_data
LEFT OUTER JOIN thing_readings
ON thing_data.id = thing_readings.thing_id
基本上,读数表中的内部联接并没有从INNER JOIN thing_data on thing_id = thing_data.id
中受益,并且实际上正在对阅读表中的所有行进行扫描。
是否有可能让优化器注意到我已将thing_data
记录集限制为仅一行,因此使后续加入快速而非超慢?
编辑:对匿名查询不好的道歉。
我创建了一个SQLFiddle来演示我遇到的问题 - 我仍然需要添加2个WHERE
子句(不利于代码可维护性等) - 即使忘记了CTE并使用常规连接表,问题仍然存在克雷格建议道。我更习惯于在转换模式时没有出现此问题的SQL Server。
答案 0 :(得分:4)
是否有可能让优化器注意到我已将thing_data记录集限制为仅一行,因此使后续连接快速而非超慢?
不在PostgreSQL中,至少在9.4或更早版本中。希望以后会改变。
PostgreSQL中的CTE是优化围栏 - 实质上,规划者不能将限定符推入其中,或者将限定符拉出来。
如果这是一个问题,你需要回到使用旧式FROM
子句中的子查询。
SELECT thing_data.*
FROM (
SELECT * FROM things WHERE id = '0000000001'
) data_thing
LEFT OUTER JOIN (
SELECT thing_timestamp
FROM reading_log_instantaneous_schedule
INNER JOIN thing_data on thing_id = thing_data.id
ORDER BY thing_timestamp DESC LIMIT 1)
thing_readings
ON thing_data.id = thing_readings.thing_id;
因为FROM
中的子查询允许限定符下推/上拉。但是,在这种情况下,您确实希望横向应用WHERE
子句。这最好通过进一步简化,摆脱子查询来完成:
SELECT thing_data.*
FROM things
LEFT OUTER JOIN (
SELECT thing_timestamp
FROM reading_log_instantaneous_schedule
INNER JOIN thing_data on thing_id = thing_data.id
ORDER BY thing_timestamp DESC LIMIT 1)
thing_readings
ON thing_data.id = thing_readings.thing_id
WHERE things.id = '0000000001'
但是,整个过程似乎是一种非常复杂的方式(基于您的SQLFiddle http://sqlfiddle.com/#!15/17c82/3):
SELECT things.*, thingreadings.reading
FROM things
LEFT OUTER JOIN thingreadings ON thingreadings.thingid = things.id
WHERE things.id = '1'
ORDER BY reading DESC LIMIT 1;