不久之前,我发布了一条关于在MySQL中优化查询的消息。我已经将数据和查询移植到PostgreSQL,但现在PostgreSQL有同样的问题。 MySQL中的解决方案是强制优化器不使用STRAIGHT_JOIN进行优化。 PostgreSQL没有这样的选择。
更新修订
我已经隔离了修复问题的查询部分(d.month_ref_id = 1
):
select
d.*
from
daily d
join month_ref m on m.id = d.month_ref_id
join year_ref y on y.id = m.year_ref_id
where
m.category_id = '001' and
d.month_ref_id = 1
但是,我不能硬编码一个月的1
引用。生成全表扫描的查询是:
select
d.*
from
daily d
join month_ref m on m.id = d.month_ref_id
join year_ref y on y.id = m.year_ref_id
where
m.category_id = '001'
daily.month_ref_id
上的索引是:
CREATE INDEX daily_month_ref_idx
ON climate.daily
USING btree
(month_ref_id);
为什么查询执行全表扫描以及可以采取哪些措施来避免它?
谢谢!
答案 0 :(得分:3)
Select avg(d.amount) AS amount, y.year
From (station s
Left Join city c -- You want to cross join on city? Why not use an Inner join?
On c.id = 10663
And 6371.009
* SQRT(
POW(RADIANS(c.latitude_decimal - s.latitude_decimal), 2)
+ (
COS(RADIANS(c.latitude_decimal + s.latitude_decimal) / 2)
* POW(RADIANS(c.longitude_decimal - s.longitude_decimal), 2)
)
) <= 50)
Join station_district sd
On sd.Id = s.station_district_id
Join year_ref y
On y.station_district_id = sd.id
Join month_ref m
On m.year_ref_id = y.id
Join daily d
On d.month_ref_id = m.id
Where s.elevation Between 0 And 2000
And y.year Between 1980 And 2000
And m.month = 12
And m.category_id = '001'
And d.daily_flag_id <> 'M'
Group By y.year
由于您未在结果中使用station,station_district或city表,因此您可以将它们移动到exists语句中:
Select avg(d.amount) AS amount, y.year
From year_ref y
Join month_ref m
On m.year_ref_id = y.id
Join daily d
On d.month_ref_id = m.id
Where y.year Between 1980 And 2000
And m.month = 12
And m.category_id = '001'
And d.daily_flag_id <> 'M'
And Exist (
Select 1
From station s1
Join city c1
On c1.id = 10663
Where 6371.009
* SQRT(
POW(RADIANS(c1.latitude_decimal - s1.latitude_decimal), 2)
+ (
COS(RADIANS(c1.latitude_decimal + s1.latitude_decimal) / 2)
* POW(RADIANS(c1.longitude_decimal - s1.longitude_decimal), 2)
)
) <= 50
And S1.station_district_id = y.station_district_id
)
Group By y.year
答案 1 :(得分:1)
我不知道您尝试过的查询的其他变体,但是城市上的JOIN似乎有点奇怪 - 您是否尝试用WHERE子句替换它?此外,各种表之间的关系当前在WHERE子句中 - 这些可能最好实现为INNER JOIN。
免责声明:我不具体了解PostreSQL。
编辑:这是一个描述将JOERE子句更改为影响连接顺序的JOERE子句的链接,并讨论了join_collapse_limit以强制优化器使用您指定的连接顺序。 http://www.postgresql.org/docs/8.2/static/explicit-joins.html
EDIT2:另一种替代方法是嵌套SELECT语句,这也可能迫使优化器以您指定的(反向)嵌套顺序构造查询。
答案 2 :(得分:0)
我认为由于您对查询/连接进行参数化的方式,FTS正在发生。通过这个,我的意思是你有两个参数,一个是与'daily'表中的一个列进行比较,另一个是与'month-ref'表中的一个列进行比较。但是,这两个值都可用于过滤单个表'month-ref'中的行。将该表作为查询中的主表,并按如下方式重写您的查询:
select
d.*
from month_ref m
join daily d on d.month_ref_id = m.id
join year_ref y on y.id = m.year_ref_id
where
m.category_id = '001' and
m.id = 1
这样,数据库可以根据输入参数值轻松找到month-ref表中的所有必要行,并且可以使用您描述的索引轻松地将每日表中的行放在指定的连接上。 根据month-ref表中可能找到的行数,以及上面引用的列中的任何一个是否包含不同的值,您可能需要在month-ref表上创建索引。