Ben*_*ess 2 mysql query mysql-5.7
我有一个 MySQL 数据库临时表,其中包含一堆针对系统 ID 和日期(它们组合构成主键)的仪表读数。
某些条目的仪表读数为 NULL,在这种情况下,它们需要更新以具有与前一天相同的仪表读数。
还有一个“差异”列,需要有当天读数和前一天读数之间的差异。
我知道如何使用代码更新表,我想知道是否有使用 SQL 的好方法。多个查询很好(我猜在计算差异之前至少需要更新 NULL 读数)。
这是一个带有假数据的示例架构,也在这个 sqlfiddle 中:
CREATE TABLE Readings (`SystemID` INT, `ReadingDate` DATE, `Reading` INT NULL, `Diff` INT NULL, PRIMARY KEY(`SystemID`,`ReadingDate`));
INSERT INTO Readings VALUES
(1, '2021-02-01', 24, NULL),
(1, '2021-02-02', 26, NULL),
(1, '2021-02-03', 23, NULL),
(1, '2021-02-04', NULL, NULL),
(1, '2021-02-05', NULL, NULL),
(1, '2021-02-06', 28, NULL),
(1, '2021-02-07', 21, NULL),
(2, '2021-02-01', 124, NULL),
(2, '2021-02-02', 126, NULL),
(2, '2021-02-03', 123, NULL),
(2, '2021-02-04', NULL, NULL),
(2, '2021-02-05', NULL, NULL),
(2, '2021-02-06', 128, NULL),
(2, '2021-02-07', 121, NULL);
Run Code Online (Sandbox Code Playgroud)
运行更新后,值将是(保留插入格式以使其更易于查看):
(1, '2021-02-01', 24, NULL),
(1, '2021-02-02', 26, 2),
(1, '2021-02-03', 23, -3),
(1, '2021-02-04', 23, 0),
(1, '2021-02-05', 23, 0),
(1, '2021-02-06', 28, 5),
(1, '2021-02-07', 21, -7),
(2, '2021-02-01', 124, NULL),
(2, '2021-02-02', 126, 2),
(2, '2021-02-03', 123, -3),
(2, '2021-02-04', 123, 0),
(2, '2021-02-05', 123, 0),
(2, '2021-02-06', 128, 5),
(2, '2021-02-07', 121, -7);
Run Code Online (Sandbox Code Playgroud)
编辑:更复杂的是,这是实际应用程序中的临时表。
如果您使用的是 MySQL >= 8.0
您可以利用LATERAL派生表。
SELECT
Act.SystemID,
Act.ReadingDate,
Act.Reading,
Act.Diff,
Prev.Reading as PrevReading
FROM
Readings AS Act,
LATERAL
(SELECT Reading
FROM Readings
WHERE SystemID = Act.SystemID
AND ReadingDate < Act.ReadingDate
AND Reading IS NOT NULL
ORDER BY SystemID, ReadingDate DESC
LIMIT 1) Prev
ORDER BY
Act.SystemID, Act.ReadingDate;
Run Code Online (Sandbox Code Playgroud)
在这种情况下,它返回不为空的先前读数。
系统ID | 阅读日期 | 阅读 | 差异 | 上一篇阅读
-------: | :---------- | ------: | ---: | ----------:
1 | 2021-02-02 | 26 | 空| 24
1 | 2021-02-03 | 23 | 空| 26
1 | 2021-02-04 | 空| 空| 23
1 | 2021-02-05 | 空| 空| 23
1 | 2021-02-06 | 28 | 空| 23
1 | 2021-02-07 | 21 | 空| 28
2 | 2021-02-02 | 126 | 空| 124
2 | 2021-02-03 | 123 | 空| 126
2 | 2021-02-04 | 空| 空| 123
2 | 2021-02-05 | 空| 空| 123
2 | 2021-02-06 | 128 | 空| 123
2 | 2021-02-07 | 121 | 空| 128
现在,您可以使用此语法在单个查询中更新所有行:
UPDATE
Readings Act,
LATERAL
(SELECT Reading
FROM Readings
WHERE SystemID = Act.SystemID
AND ReadingDate < Act.ReadingDate
AND Reading IS NOT NULL
ORDER BY SystemID, ReadingDate DESC
LIMIT 1) Prev
SET
Act.Reading = IF(Act.Reading IS NULL, Prev.Reading, Act.Reading),
Act.Diff = CASE
WHEN Act.Reading IS NULL THEN 0
ELSE Act.Reading - Prev.Reading
END;
SELECT * FROM Readings ORDER BY SystemID, ReadingDate;
Run Code Online (Sandbox Code Playgroud)
这就是结果:
系统ID | 阅读日期 | 阅读 | 差异
-------: | :---------- | ------: | ---:
1 | 2021-02-01 | 24 | 空值
1 | 2021-02-02 | 26 | 2
1 | 2021-02-03 | 23 | -3
1 | 2021-02-04 | 23 | 0
1 | 2021-02-05 | 23 | 0
1 | 2021-02-06 | 28 | 5
1 | 2021-02-07 | 21 | -7
2 | 2021-02-01 | 124 | 空值
2 | 2021-02-02 | 126 | 2
2 | 2021-02-03 | 123 | -3
2 | 2021-02-04 | 123 | 0
2 | 2021-02-05 | 123 | 0
2 | 2021-02-06 | 128 | 5
2 | 2021-02-07 | 121 | -7
db<>在这里摆弄
如果您使用的是 MySQL 5.7
您可以创建一个函数,返回相应的先前阅读:
CREATE FUNCTION PrevReading(sID int, rDate date)
RETURNS INT
BEGIN
DECLARE val INT;
SET val := (SELECT Reading
FROM Readings
WHERE SystemID = sID
AND ReadingDate < rDate
AND Reading IS NOT NULL
ORDER BY
SystemID, ReadingDate DESC
LIMIT 1);
RETURN val;
END;
Run Code Online (Sandbox Code Playgroud)
然后更新你的表:
UPDATE
Readings
SET
Reading = IF(Reading IS NULL, PrevReading(SystemID, ReadingDate), Reading),
Diff = IF(Reading IS NULL, 0, Reading - PrevReading(SystemID, ReadingDate))
Run Code Online (Sandbox Code Playgroud)
db<>在这里摆弄