简体   繁体   中英

Replacing NULL value in current row based on 'closest' matching 'earlier' row. (date based table)

I have the following mysql table

+---------------------+------+
| time                | val  |
+---------------------+------+
| 2005-02-03 00:00:00 | 2.11 |
| 2005-02-04 00:00:00 | 2.11 |
| 2005-02-05 00:00:00 | NULL |
| 2005-02-06 00:00:00 | NULL |
| 2005-02-07 00:00:00 | 3.43 |
| 2005-02-08 00:00:00 | NULL |
| 2005-02-09 00:00:00 | NULL |
| 2005-02-10 00:00:00 | 5.66 |
| 2005-02-11 00:00:00 | 5.66 |
| 2005-02-12 00:00:00 | NULL |
+---------------------+------+

I want to create an algorithm (in PHP) that fill the NULL values based on the last non-null value. So the table will become the following

+---------------------+------+
| time                | val  |
+---------------------+------+
| 2005-02-03 00:00:00 | 2.11 |
| 2005-02-04 00:00:00 | 2.11 |
| 2005-02-05 00:00:00 |>2.11 |
| 2005-02-06 00:00:00 |>2.11 |
| 2005-02-07 00:00:00 | 3.43 |
| 2005-02-08 00:00:00 |>3.43 |
| 2005-02-09 00:00:00 |>3.43 |
| 2005-02-10 00:00:00 | 5.66 |
| 2005-02-11 00:00:00 | 5.66 |
| 2005-02-12 00:00:00 |>5.66 |
+---------------------+------+

I'm looking for clues on how to approach this situation. I'm using PHP-Laravel.

There is an SQLFiddle here for 'standard' SQL.

As comments indicate, you should be fixing this when you populate the table. That said, it can be done in PHP or MySQL. Here is one option:

SET @x:=0;
SELECT `time`, IF(val IS NOT NULL, @x:=val, @x) AS val
FROM yourtable
ORDER BY `time`;

Bear in mind that your result will change depending on ordering and WHERE and so on. Use SET @x:=0; to define your default value for cases when first row has NULL value.

If you need to fix the data permanently, rather than for single query, you can update the table with 'correct' values:

SET @x:=0;
UPDATE yourtable SET val=IF(val IS NOT NULL, @x:=val, @x) 
 ORDER BY `time`;

If you want to use just SQL, you could use this UPDATE query:

UPDATE
  tab INNER JOIN (
    SELECT
      t1.time,
      MAX(t2.time) prev_time
    FROM
      tab t1 INNER JOIN tab t2
      ON t1.time>t2.time
    WHERE
      t1.val IS NULL
      AND t2.val IS NOT NULL
    GROUP BY
      t1.time
  ) p ON tab.time = p.time
  INNER JOIN tab t2 ON p.prev_time = t2.time
SET
  tab.val = t2.val

Please see a fiddle here .

The subquery will return, for every NULL row the previous date with a non-NULL value. Then I'm joining the table with itself to get the value and to update the null row.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM