我对MySQL并不陌生,但我绝对会想到这一点。
我想根据日期和大气水平显示巴拿马和伯利兹的气温差异表。该查询应该根据日期和大气水平匹配巴拿马和伯利兹数据,并返回前30个差异,按差异程度排序。
然而,它非常缓慢(超过30秒)所以它超时了。我为这个数据集编写的其他一些查询也非常慢(大约26s)。但是,如果我只运行子查询,它们只需要1.7秒左右。我应该注意到,下面的两个表都超过了440,000行,但我并不认为它非常大。问题可能是我加入表格的方式或我创建子查询的方式。
这是我的设置:(它是导出表格中的SQL。我省略了一些列)
/**The table for Panama weather data */
CREATE TABLE `panama_weather_data` (
`Id` varchar(40) NOT NULL,
`OwmPackageId` varchar(30) NOT NULL,
`Level` FLOAT DEFAULT NULL,
`Dt` date DEFAULT NULL,
`Temperature` float DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `panama_weather_data`
ADD PRIMARY KEY (`Id`) USING BTREE;
COMMIT;
/**The table for Belize weather data*/
CREATE TABLE `belize_weather_data` (
`Id` varchar(40) NOT NULL,
`OwmPackageId` varchar(30) NOT NULL,
`Level` FLOAT DEFAULT NULL,
`Dt` date DEFAULT NULL,
`Temperature` float DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `belize_weather_data`
ADD PRIMARY KEY (`Id`) USING BTREE;
COMMIT;
/**Code to populate the tables here*/
这是我的疑问:
SELECT ABS(PanamaTemperature-BelizeTemperature) AS TemperatureDif,
PanamaAtmostphericLevel, PanamaTable.Dt
FROM
(SELECT CAST(panama_weather_data.Dt AS DATETIME) AS Dt,
panama_weather_data.Level AS PanamaAtmostphericLevel,
panama_weather_data.Temperature AS PanamaTemperature
FROM panama_weather_data
WHERE panama_weather_data.OwmPackageId = 'openweathermappkg19758' )
AS PanamaTable
JOIN
(SELECT CAST(belize_weather_data.Dt AS DATETIME) AS Dt,
belize_weather_data.Level AS BelizeAtmosphericLevel,
belize_weather_data.Temperature AS BelizeTemperature
FROM belize_weather_data
WHERE belize_weather_data.OwmPackageId = 'openweathermappkg19758' )
AS BelizeTable
ON PanamaAtmostphericLevel = BelizeAtmosphericLevel
AND PanamaTable.Dt = BelizeTable.Dt
ORDER BY TemperatureDif
LIMIT 30
我的问题是:无论如何要优化此查询并减少痛苦吗?
答案 0 :(得分:1)
CAST(panama_weather_data.Dt AS DATETIME)AS Dt
为什么呢? (所有这一切都会减慢查询速度)
无论如何都要优化此查询
您向我们展示的SQL SELECT语句肯定会不成为我的起点。但是,您没有告诉我们您将来如何查询数据。具体来说,您是否真的要在每次运行查询时检查所有数据?
您最大的胜利来自于不将数据保存在单独的表中 - 它应该是一个包含两个数据集的不同属性的表。
在那之后,下一个最大的改进将来自于存储表中的温度差并将其编入索引。
答案 1 :(得分:0)
在SQL数据库中大幅提高速度的一种方法是使用索引。这是磁盘空间和查询性能之间的权衡。
要找出放置索引的位置,请搜索限制结果集的条件。在您的情况下,两个表可能有几十万行,但您只需要其中30个,其大气水平和日期相等。你可能想在这两列上放一个索引,如下所示:
CREATE INDEX level_date_panama ON panama_weather_data (Level, Dt);
CREATE INDEX level_date_belize ON belize_weather_data (Level, Dt);
请告诉我这是否会提高您的表现。
答案 2 :(得分:0)
你可以做一些事情来改善绩效:
根据您发布的内容,我看不出为什么子查询对于连接是必要的。您可以轻松地删除它们并使用实际的列名称重写,以代替您编写AS
值的位置。
CAST
并不是一个特别昂贵的运营商,但确实需要时间来完成。如果您仅将这些列用作日期时间,则应该按原样输入它们并将列类型更改为Datetime。您可以直接比较这些值,而不必投射它们。
离开(2),如果你所有的Dt值都是Dates,那么将它们转换为Datetimes不会对该值做任何事情,所以只需比较自然日期类型。
如果由于外部限制而无法进行上述操作,请根据您的加入方式创建索引,这将是ON子句中使用的列。
答案 3 :(得分:0)
id
中有哪些值?也许你可以摆脱id
,并使用PRIMARY KEY(level, dt)
?
为什么level
为FLOAT
?如果它们真的是“浮动”值,那么两个表具有相同的值是否切合实际?我猜他们是海拔还是米?在这种情况下,MEDIUMINT UNSIGNED
不会满足吗?
则...
SELECT ABS(p.Temperature - b.Temperature) AS TemperatureDif,
p.Level,
p.Dt
FROM panama_weather_data AS p
JOIN belize_weather_data AS b
USING (OwmPackageId, Level, Dt)
WHERE p.OwmPackageId = 'openweathermappkg19758'
ORDER BY TemperatureDif DESC
LIMIT 30;
你需要
INDEX(OwmPackageId, Level, Dt)
以任何顺序列出这些列,以及任何一个(或两个)表。
如前所述,不需要CAST
。但是,如果您需要"2017-08-13 10:04:12"
以外的某种格式,请在DATE_FORMAT(...)
子句中使用SELECT
(而不是USING
子句。)
不要让两个“相同”的表格,而是考虑让一个表格包含一个涉及哪个国家/地区的额外列。这样可以很容易地扩展到任意数量的位置。 SELECT
需要是“自联接”,语法会略有不同。