如何按速度优化 mysql 的日期组?

How can I optimize mysql's date group by speed?

CREATE TABLE `device_m1000` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`sensor_id` MEDIUMINT(9) NOT NULL,
`ctime` DATETIME NOT NULL,
`now_data` FLOAT(12) NOT NULL,
`total_data` FLOAT(12) NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `sensor_id` (`sensor_id`) USING BTREE,
INDEX `ctime` (`ctime`) USING BTREE) COLLATE='utf8_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=296533;

上面table中有30万条数据。 total_data 列总是出现在累计使用情况中。 (一个总是增加的结构) 如果我想知道“今天”的用法,我根据今天的日期取最小值和最大值并减去它。

尝试sql

SELECT ROUND((max.v - min.v), 2) total
FROM   (SELECT
               DATE(ctime) `date`,
               sum(total_data)  v
        FROM   device_m1000
        WHERE  ( sensor_id, ctime ) IN (SELECT sensor_id,
                                               MAX(ctime) AS dt
                                        FROM   device_m1000
                                        WHERE  ctime >= '2020-11-23 06:30' and ctime < '2020-11-24 06:30'
                                        GROUP  BY sensor_id
                                        ORDER  BY null)
        group by `date`) max
       INNER JOIN (SELECT
                          DATE(ctime) `date`,
                          sum(total_data)  v
                   FROM   device_m1000
                   WHERE  ( sensor_id, ctime ) IN (SELECT sensor_id,
                                                          MIN(ctime) AS dt
                                                   FROM   device_m1000
                                                   WHERE  ctime >= '2020-11-23 06:30' and ctime < '2020-11-24 06:30'
                                                   GROUP  BY sensor_id
                                                   ORDER  BY null)
                   group by `date`) min
               ON min.`date` = max.`date`;

使用以下查询获取数据。 但是,它在 max(ctime) 的代码中只是延迟了 10 多秒。 我该如何优化它?

我了解到您想要每个传感器今天最迟和最早 total_data 之间的差异。如果是这样,您可以像这样使用 window 函数:

select sensor_id, sum(case when rn_desc = 1 then total_data else - total_data end) as total_diff
from (
    select d.*, 
        row_number() over(partition by sensor_id order by ctime) rn_asc,
        row_number() over(partition by sensor_id order by ctime desc) rn_desc
    from device_m1000
    where ctime >= current_date and ctime < current_date + interval 1 day
) t
where 1 in (rn_asc, rn_desc)
group by sensor_id

实际上如果 total_data 总是增加,这更简单(并且适用于所有 MySQL 版本):

select sensor_id, max(total_data) - min(total_data) as total_diff
from device_m1000
where ctime >= current_date and ctime < current_date + interval 1 day
group by sensor_id

那我推荐如下索引:(ctime, sensor_id, total_data).

  • “行构造函数”表现不佳。 (WHERE (a,b) IN ...)
  • FLOAT(12)不给你12个有效数字,你只得到大约7个。
  • id有用吗?还是 (sensor_id, ctime) 是独一无二的?如果它是唯一的,将其设为 PRIMARY KEY 并去掉 id.
  • PK不了就INDEX(sensor_id, ctime).

回到你的问题。放弃将小计放入每一行的尝试。实际上,任何方法每秒都将执行超过 10 亿次操作——因为必须为每个传感器汇总当天的大约 30,000 个项目。

而是制作一个单独的“摘要 table”,其中包含当天的日期、传感器和总计。然后,当你 INSERT 进入细节时 table (就像你拥有的那样),也做

INSERT INTO daily_summary (date, sensor, tot)
    VALUES (?, ?, ?)
    ON DUPLICATE KEY UPDATE
        tot = VALUES(tot) + ?

并使用 DOUBLE 作为 tot。并且有 PRIMARY KEY(sensor, date)

这避免了减法并提高了效率。

有关摘要的更多信息 tables:http://mysql.rjweb.org/doc.php/summarytables

如果您需要每小时小计,则将其设为每小时 table 而不是每天。您可以通过从每小时摘要中添加 24 行来有效地获得每日金额 table.