我有以下数据库架构: https://dl.dropbox.com/u/37915176/schema.PNG
我在meter_relevation中有超过2百万条记录。数据来自几个电表,这些电表与具有meter_history表的设备(设备)相关联。
我尝试获取特定日期范围的设备数据:
SELECT MR.*
FROM device AS D, meter_history AS MH, meter AS M, meter_relevation AS MR
WHERE D.Id=MH.Id_Device
AND MH.Id_Meter=M.Id
AND M.Id=MR.Id_Meter
AND D.Id="8"
AND MR.Date>="2012-10-04"
AND MR.Date<="2012-10-04"
但是性能非常慢,即使指定的日期范围没有记录,我也可以获得10秒。
我尝试使用EXPLAIN,我清楚地看到我的查询不是最优的,在meter_relevation表上列出了总行数,超过2百万:https://dl.dropbox.com/u/37915176/explain.png
有什么建议吗?有一个更好的方法?当然,我可以在客户端做一些工作,并分成几个查询。但我想知道单个SELECT查询是否有更好的方法。
答案 0 :(得分:1)
我建议您尝试在列上添加索引:
D.Id
MH.Id_Device
MH.Id_Meter
M.Id
MR.Id_Meter
MR.Date
答案 1 :(得分:1)
如果这是您正在运行的查询,则它看起来不是最佳的。你真的不需要D表,不是吗?看看MH.Id_device = "8"
如何满足设备上的条件。
但假设还有其他字段未显示,那么让我们重写:
SELECT MR.*
FROM meter_relevation AS MR
JOIN meter AS M ON ( M.Id = MR.ID_Meter )
JOIN meter_history AS MH ON ( MH.Id_Meter = M.Id )
JOIN device AS D ON ( D.Id=MH.Id_Device AND D.Id = "8" )
WHERE
MR.Date BETWEEN "2012-10-04" AND "2012-10-04";
所以我们需要索引。第一个是最重要的
CREATE INDEX mr_ndx ON meter_relevation ( Date, Id_Meter );
但是请尝试删除上面的索引并改为使用:
CREATE INDEX mr_ndx ON meter_relevation ( Id_Meter, Date );
CREATE INDEX m_ndx ON meter(Id); -- This probably already exists
CREATE INDEX mh_ndx ON meter_history( Id_Device, Id_Meter );
CREATE INDEX d_ndx ON device (Id); -- This too probably already exists
上面的内容,如果这样编写,就等同于(但MySQL应该意识到这一点,所以我认为这不会让你的速度减慢太多)
SELECT MR.*
FROM meter_relevation AS MR
JOIN meter AS M ON ( MR.ID_Meter = M.Id)
JOIN meter_history AS MH ON (MH.Id_Device = "8" AND MH.Id_Meter = M.Id)
WHERE
MR.Date BETWEEN "2012-10-04" AND "2012-10-04";
答案 2 :(得分:0)
我的猜测是因为缺少索引而正在进行表扫描。尝试注释掉这一位,看看它是否有所改善:
AND MR.Date>="2012-10-04"
AND MR.Date<="2012-10-04"
如果是这样,那么在MR.Date上尝试索引。
答案 3 :(得分:0)
索引肯定会有所帮助。适当地运行EXPLAIN和索引。索引所有外键和日期字段。这里有关于JOIN索引的更多细节。
通常,最简单的解决方案是避免过于复杂的查询。你加入了4张桌子。你真的需要来自所有4个表的所有数据吗?我通常会避免使用SELECT *查询来强制自己确保我只获取所需的数据。