首先,我想说的是数据库的设计已经提供给我,并且我被要求提高查询的性能。我不应该修改表的字段(我相信将一些字符移动到int会提高性能......)。
该表有多行(我大约有14 * 10 ^ 6行,而且还在增长......)
我想提高像这样的查询的性能:
SELECT
timestampms/1000 as secs,
round(avg(data),2) as data,
FROM_UNIXTIME(timestampms/1000,'%m/%Y') as control
FROM externalsensor
WHERE (model='electric' and id=4 AND sensorid='TRI1VA' AND rawdata < 65535 )
AND timestampms BETWEEN '1272454583000' AND '1303990583000'
GROUP BY control
ORDER BY secs ASC;
上述表格是:
CREATE TABLE `externalsensor` (
`model` varchar(50) NOT NULL DEFAULT 'desconocido',
`timestampms` char(13) DEFAULT NULL,
`amtype` smallint(5) unsigned DEFAULT NULL,
`id` smallint(5) unsigned DEFAULT NULL,
`version` smallint(5) unsigned DEFAULT NULL,
`interval` smallint(5) unsigned DEFAULT NULL,
`counter` int(10) unsigned DEFAULT NULL,
`sensorid` varchar(20) DEFAULT NULL,
`rawdata` smallint(5) unsigned DEFAULT NULL,
`data` decimal(20,10) DEFAULT NULL,
KEY `temps` (`timestampms`),
KEY `sensor` (`model`,`id`,`sensorid`,`timestampms`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
解释的选择显示:
+----+-------------+----------------+-------+---------------+--------+---------+------+--------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------+-------+---------------+--------+---------+------+--------+----------------------------------------------+
| 1 | SIMPLE | externalsensor | range | temps,sensor | sensor | 92 | NULL | 194443 | Using where; Using temporary; Using filesort |
+----+-------------+----------------+-------+---------------+--------+---------+------+--------+----------------------------------------------+
查询花了 20秒,我想知道是否有人看到了改进查询的方法。感谢任何建议:)
更新
经过所有令人敬畏的建议,以及这些家伙给我的MySql的这一课,我决定做以下事情:
因为我想要的是按月(一年),几周(一年)和一小时(一天)的数据的摘要,我决定创建一个< strong>摘要表,包含所有信息,并由cron中的脚本自动更新。
通过这样做,我不是在查询 200.000行,我相信它会有所改善。当然,我将考虑所有关于索引的建议,char-&gt; int以及这些人提出的所有建议。
我还没有开始开发,但是一旦完成并且我有了新的结果,我将与他们一起更新这篇文章。
我希望我能接受所有答案,但这是不可能的......谢谢大家。
答案 0 :(得分:3)
尝试通过将计算存储在表中来从select中删除计算。
在您的情况下,timestampms/1000 as secs
和FROM_UNIXTIME(timestampms/1000,'%m/%Y') as control
可以在 secs 字段和控制字段中预先计算,并填充ON INSERT和ON UPDATE的触发器即可。你不修改字段,你添加一些。
'control'用于GROUP BY,因此通过在此新控件列上添加索引可以获得很好的好处。 'secs'用于排序,因此同样适用。您甚至可能需要基于这两列的一些索引。
答案 1 :(得分:2)
将timestampms
的类型更改为bigint(15) unsigned
- 您当前有5个表达式需要MySQL在计算之前将该字段从字符串转换为整数。正如Pentium10所建议的那样 - 做ORDER BY timestampms
。
此外,您无需在rawdata < 65535
语句中指定WHERE
,因为65535是无符号smallint的最大值。
答案 2 :(得分:2)
这里有几个问题:
修复它:
答案 3 :(得分:1)
按真实场(时间戳)排序,而非计算字段。
尝试使用“GROUP BY”timestampms / 1000“而不是”control“。
在mysql ui上,单独查询这些:
WHERE model ='electric'
WHERE id = 4
WHERE sensorid ='TRI1VA'
WHERE rawdata&lt; 65535
时间戳在'1272454583000'和'1303990583000'之间的时间戳
然后返回最少的行,请确保在第一个位置的字段上有索引。
这是一种粗略的方法,您可以从那里向索引添加字段。
SELECT列表中的计算表达式不会有任何区别。如果你需要,where子句中的“rawdata”就可以了。
你真的需要它返回194,000行吗?在任何情况下,获取许多将花费相当长的时间。
答案 4 :(得分:0)
根据某些条件(例如时间),您需要分区您需要的数据量。
您仍然可以通过索引进行改进。
SELECT timestampms / 1000 AS secs,
Round(Avg(data), 2) AS data,
From_unixtime(timestampms / 1000, '%m/%Y') AS control
FROM externalsensor
WHERE ( model = 'electric'
AND id = 4
AND sensorid = 'TRI1VA'
AND rawdata < 65535 )
AND timestampms BETWEEN '1272454583000' AND '1303990583000'
GROUP BY control
ORDER BY timestampms ASC;
创建一个包含大量数据集的样本表,然后尝试使用此复合索引
index(model,id,sensorid,rawdata,timestampms,control,data)
回复解释输出。