我使用Raspberry Pi管理我的家庭自动化系统并收集大量数据(温度,水位,能源使用......),主要是每分钟。由于Pi不是很强大,我通过实现缓存表来优化我的Web前端查询,缓存表聚合每小时和每日值来加速一切。我最近的补充是使用触发器自动传播INSERT
和UPDATE
语句。每次将值插入(或更新)到water_level
表中时,其触发器将触发并正确计算触发事件发生的小时的开始和结束。此外,min
和max
值已正确计算并插入water_level_hourly
表中。查看表格时,min
和max
值会按预期显示。
问题从我添加到表water_level_hourly
的第二个触发开始。其目的是每次在每小时表中插入或更新某些内容时,将所有小时值聚合为每日值。我从第一个表中复制/粘贴了触发器,但更改了当前日期的计算(这是插入/更新行的时间的转换为DATE
)。不知何故,每当触发器触发时,它都无法从每小时表中正确查询min
和max
的值,并始终将-1
插入每日表中。我需要更改什么才能实现自动计算每日值的预期行为?
我在下面添加了一个示例(我知道我可以将触发器合并到一个存储过程中,但是想先找到问题,所以请耐心等待):
CREATE DATABASE IF NOT EXISTS `homebusdata` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `homebusdata`;
-- --------------------------------------------------------
CREATE TABLE IF NOT EXISTS `water_level` (
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`level` int(11) NOT NULL,
PRIMARY KEY (`time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
DROP TRIGGER IF EXISTS `trg_water_level_insert`;
DELIMITER //
CREATE TRIGGER `trg_water_level_insert` AFTER INSERT ON `water_level`
FOR EACH ROW BEGIN
DECLARE t1, t2 TIMESTAMP;
DECLARE min, max FLOAT;
# calculate start and end of current hour
SET @t1 := DATE_FORMAT(NEW.`time`, '%Y-%m-%d %H:00:00');
SET @t2 := DATE_ADD(@t1, INTERVAL 1 HOUR);
# fetch min/max values of current hour
SELECT MIN(`level`), MAX(`level`)
INTO @min, @max
FROM `water_level`
WHERE `water_level`.`time` BETWEEN @t1 AND @t2;
# care for empty values at beginning of each hour
IF @min IS NULL THEN
SET @min := 0;
END IF;
IF @max IS NULL THEN
SET @max := 0;
END IF;
# write min/max to hourly table
INSERT INTO `water_level_hourly` (`time`, `min`, `max`)
VALUES (@t1, @min, @max)
ON DUPLICATE KEY UPDATE `min`=@min, `max`=@max;
END
//
DELIMITER ;
DROP TRIGGER IF EXISTS `trg_water_level_update`;
DELIMITER //
CREATE TRIGGER `trg_water_level_update` AFTER UPDATE ON `water_level`
FOR EACH ROW BEGIN
DECLARE t1, t2 TIMESTAMP;
DECLARE min, max FLOAT;
# calculate start and end of current hour
SET @t1 := DATE_FORMAT(NEW.`time`, '%Y-%m-%d %H:00:00');
SET @t2 := DATE_ADD(@t1, INTERVAL 1 HOUR);
# fetch min/max values of current hour
SELECT MIN(`level`), MAX(`level`)
INTO @min, @max
FROM `water_level`
WHERE `water_level`.`time` BETWEEN @t1 AND @t2;
# care for empty values at beginning of each hour
IF @min IS NULL THEN
SET @min := 0;
END IF;
IF @max IS NULL THEN
SET @max := 0;
END IF;
# write min/max to hourly log
INSERT INTO `water_level_hourly` (`time`, `min`, `max`)
VALUES (@t1, @min, @max)
ON DUPLICATE KEY UPDATE `min`=@min, `max`=@max;
END
//
DELIMITER ;
-- --------------------------------------------------------
CREATE TABLE IF NOT EXISTS `water_level_hourly` (
`time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`min` int(11) NOT NULL,
`max` int(11) NOT NULL,
PRIMARY KEY (`time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Trigger `water_level_hourly`
--
DROP TRIGGER IF EXISTS `trg_water_level_hourly_insert`;
DELIMITER //
CREATE TRIGGER `trg_water_level_hourly_insert` AFTER INSERT ON `water_level_hourly`
FOR EACH ROW BEGIN
DECLARE t DATE;
DECLARE min, max FLOAT;
# create date value for current day
SET @t := DATE(NEW.`time`);
# get min/max value for current day
SELECT MIN(`min`), MAX(`max`)
INTO @min, @max
FROM `water_level_hourly`
WHERE DATE(`water_level_hourly`.`time`) = @t;
# care for empty values at beginning of each day
IF @min IS NULL THEN
SET @min := -1;
END IF;
IF @max IS NULL THEN
SET @max := -1;
END IF;
# write min/max into daily log
INSERT INTO `water_level_daily` (`time`, `min`, `max`)
VALUES (@t, @min, @max)
ON DUPLICATE KEY UPDATE `min`=@min, `max`=@max;
END
//
DELIMITER ;
DROP TRIGGER IF EXISTS `trg_water_level_hourly_update`;
DELIMITER //
CREATE TRIGGER `trg_water_level_hourly_update` AFTER UPDATE ON `water_level_hourly`
FOR EACH ROW BEGIN
DECLARE t DATE;
DECLARE min, max FLOAT;
# create date value for current day
SET @t := DATE(NEW.`time`);
# get min/max value for current day
SELECT MIN(`min`), MAX(`max`)
INTO @min, @max
FROM `water_level_hourly`
WHERE DATE(`water_level_hourly`.`time`) = @t;
# care for empty values at beginning of each day
IF @min IS NULL THEN
SET @min := -1;
END IF;
IF @max IS NULL THEN
SET @max := -1;
END IF;
# write min/max into daily log
INSERT INTO `water_level_daily` (`time`, `min`, `max`)
VALUES (@t, @min, @max)
ON DUPLICATE KEY UPDATE `min`=@min, `max`=@max;
END
//
DELIMITER ;
-- --------------------------------------------------------
CREATE TABLE IF NOT EXISTS `water_level_daily` (
`time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`min` int(11) NOT NULL,
`max` int(11) NOT NULL,
PRIMARY KEY (`time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
以下语句应在所有三个表中创建“42”,但仅在前两个表中创建:
USE `homebusdata`;
INSERT INTO `water_level` (`time`, `level`) VALUES (NOW(), 42);
答案 0 :(得分:1)
经过一番摆弄后,我设法通过将变量min
和max
重命名为其他内容来实现它。其他变化可能没有必要。
BEGIN
DECLARE min_, max_ FLOAT;
DECLARE t DATE DEFAULT NULL;
SET t = DATE(new.time);
# get min/max value for current day
SELECT MIN(`min`), MAX(`max`)
FROM `water_level_hourly`
WHERE DATE(`water_level_hourly`.`time`) = t INTO min_, max_;
# care for empty values at beginning of each day
IF min_ IS NULL THEN
SET min_ := -1;
END IF;
IF max_ IS NULL THEN
SET max_ := -1;
END IF;
# write min/max into daily log
INSERT INTO `water_level_daily` (`time`, `min`, `max`)
VALUES (t, min_, max_)
ON DUPLICATE KEY UPDATE `min`=VALUES(min), `max`=VALUES(max);
END
不幸的是,我不确定为什么这是一个问题。我想它会被SELECT ... INTO
与变量具有相同的字段名称混淆。
答案 1 :(得分:0)
我认为这个行为是由命名变量MIN和MAx引起的,因为MIN和MAx是mysql中agreggate函数的名称。因此,当您在指令中调用MIN()函数时,分辨率堆栈会有一个带有这些名称的标量变量,而不是函数。