我正在使用以下查询填充表
来自大量读数的每日极端温度值。这里有33个温度计,每分钟读取一次读数。因此,每天大约有46K读数(即行)。但是每天只有33行被添加到extremes
。
最初,我曾想过每次插入新的读数时都会运行此查询,以便保持最新状态。但是,我很快发现此查询要花很长时间才能运行:在我的MacBook上,整整一天的读数为5½分钟。
我将对为什么它这么慢的一些见解非常感兴趣,也许是如何使这个查询更快,或者是更好的选择。注意extremes
具有Sensor_ID
和Date
作为主键,因为那是每一行的唯一特征。
谢谢!
insert into extremes(Date, Sensor_ID, `min`, `max`, `avg`)
select date(DateTime) as `Date`, Sensor_ID as Sensor_ID,
min(Value) as `min`, max(Value) as `max`, avg(Value) as `avg`
from readings where date(`DateTime`) = date(NOW())
group by date(DateTime), Sensor_ID
on duplicate key update
`min` = values(`min`), `max` = values(`max`), `avg` = values(`avg`);
根据要求,这是表格
CREATE TABLE `readings` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Sensor_ID` int(11) NOT NULL,
`DateTime` datetime NOT NULL,
`Value` double NOT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `ID_UNIQUE` (`ID`),
KEY `ID_idx` (`Sensor_ID`),
CONSTRAINT `ID` FOREIGN KEY (`Sensor_ID`) REFERENCES `sensors` (`ID`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=54500039 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `extremes` (
`Date` datetime NOT NULL,
`Sensor_ID` int(11) NOT NULL,
`min` double DEFAULT NULL,
`max` double DEFAULT NULL,
`avg` double DEFAULT NULL,
`updates` int(11) DEFAULT '0',
PRIMARY KEY (`Date`,`Sensor_ID`),
KEY `ID_idx` (`Sensor_ID`),
CONSTRAINT `foo` FOREIGN KEY (`Sensor_ID`) REFERENCES `sensors` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
答案 0 :(得分:2)
将索引添加到DateTime
表的readings
列中。
然后尝试以下SQL:
insert into extremes(Date, Sensor_ID, `min`, `max`, `avg`)
select date(DateTime) as `Date`, Sensor_ID as Sensor_ID,
min(Value) as `min`, max(Value) as `max`, avg(Value) as `avg`
from readings where `DateTime` >= date_format(curdate(), '%Y-%m-%d 00:00:00')
group by date(DateTime), Sensor_ID
on duplicate key update
`min` = values(`min`), `max` = values(`max`), `avg` = values(`avg`);
答案 1 :(得分:1)
UNIQUE KEY `ID_UNIQUE` (`ID`),
会减慢对readings
的修改。它是冗余,因为“ PRIMARY KEY ”是唯一的密钥。放下它。
仅在要插入的一行而不是所有行上执行IODKU:
insert into extremes(Date, Sensor_ID, `min`, `max`)
VALUES(... , ..., ..., ...) -- Place constants here (from the sensor)
on duplicate key update
`min` = LEAST(`min`, values(`min`)),
`max` = GREATEST(`max`, values(`max`);
然后每天晚上进行一次平均设置。
这样,您触摸的是1行,而不是1440。
另一种技术是收集读数一分钟,然后将其应用于单个查询中。
您有数百万个传感器吗?重新考虑为INT
使用4字节的Sensor_ID
;有较小的整数。
您在哪里找到那些传感器?我怀疑您是否需要超过FLOAT
的7个有效数字(4个字节)而不是8个字节的DOUBLEs
。
我对数据类型的观点是-收缩数据也可以加快处理速度,尤其是当您遇到太多数据无法缓存到RAM中的时候。
短语:“ Sensor_ID和Date作为主键”表示存在两个不同的PK,这是不可能的。而是“ Sensor_ID和Date形成复合主键”。是的,那是您需要该桌子的地方。您将Date
放在第一还是最后取决于您的典型SELECT
是什么。
FOREIGN KEYs
是另一项费用。每次执行插入操作时,都需要检查另一个表以验证ID是否存在。至此,您已经对代码进行了足够的调试; FK可以说是浪费。
avg
可以每分钟计算一次,但是(1)在一天结束之前它是毫无意义的;(2)它需要一个额外的列(带有计数) )。