如何根据前一天的值更新值?

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)

编辑:更复杂的是,这是实际应用程序中的临时表。

McN*_*ets 6

如果您使用的是 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<>在这里摆弄