在日期范围内每天创建一行?

时间:2019-02-12 13:42:28

标签: mysql sql mariadb

我有一张这样的桌子:

+----+---------+------------+
| id |  price  |    date    |
+----+---------+------------+
| 1  | 340     | 2018-09-02 |
| 2  | 325     | 2018-09-05 |
| 3  | 358     | 2018-09-08 |
+----+---------+------------+

我需要创建一个每天都有一行的视图。像这样:

+----+---------+------------+
| id |  price  |    date    |
+----+---------+------------+
| 1  | 340     | 2018-09-02 |
| 1  | 340     | 2018-09-03 |
| 1  | 340     | 2018-09-04 |
| 2  | 325     | 2018-09-05 |
| 2  | 325     | 2018-09-06 |
| 2  | 325     | 2018-09-07 |
| 3  | 358     | 2018-09-08 |
+----+---------+------------+

我可以使用带有循环(foreach)的PHP并创建一个临时变量来保持以前的价格,直到有一个新日期为止。

但是我需要提出一个观点...所以我应该使用纯SQL来做到这一点。任何想法我该怎么做?

3 个答案:

答案 0 :(得分:2)

您可以使用递归CTE在“间隙”中生成记录。为了避免最后一个日期之后的无限空白,请首先在源数据中获取最大日期,并确保不要在递归中绕过该日期。

我给您的桌子tbl打电话了

with recursive cte as (
    select     id, 
               price, 
               date, 
               (select max(date) date from tbl) mx
    from       tbl
    union all
    select     cte.id,
               cte.price, 
               date_add(cte.date, interval 1 day),
               cte.mx
    from       cte
    left join  tbl
           on  tbl.date = date_add(cte.date, interval 1 day)
    where      tbl.id is null
           and cte.date <> cte.mx
)
select   id, 
         price, 
         date
from     cte
order by 3;

demo with mysql 8

答案 1 :(得分:1)

由于您使用的是MariaDB,因此相当简单:

MariaDB [test]> SELECT  '2019-01-01' + INTERVAL seq-1 DAY  FROM seq_1_to_31;
+-----------------------------------+
| '2019-01-01' + INTERVAL seq-1 DAY |
+-----------------------------------+
| 2019-01-01                        |
| 2019-01-02                        |
| 2019-01-03                        |
| 2019-01-04                        |
| 2019-01-05                        |
| 2019-01-06                        |
(etc)

对此有一些变体,其中您生成大量的日期,但是随后使用WHERE砍切所需的内容。并将LEFT JOIN与序列“派生表”的左侧一起使用。

在查询中使用类似上面的内容作为派生表。

答案 2 :(得分:0)

这是一种无需分析功能即可工作的方法。此答案使用日历表联接方法。下面的第一个CTE是其余查询所基于的基本表。我们使用相关子查询在CTE中找到比当前日期最近的日期更早,该日期的价格为非NULL。这是找出日历表中那些未出现在原始数据集中的日期的idprice值的基础。

WITH cte AS (    
    SELECT cal.date, t.price, t.id
    FROM
    (
        SELECT '2018-09-02' AS date UNION ALL
        SELECT '2018-09-03' UNION ALL
        SELECT '2018-09-04' UNION ALL
        SELECT '2018-09-05' UNION ALL
        SELECT '2018-09-06' UNION ALL
        SELECT '2018-09-07' UNION ALL
        SELECT '2018-09-08'
    ) cal
    LEFT JOIN yourTable t
        ON cal.date = t.date
),
cte2 AS (
    SELECT
        t1.date,
        t1.price,
        t1.id,
        (SELECT MAX(t2.date) FROM cte t2
         WHERE t2.date <= t1.date AND t2.price IS NOT NULL) AS nearest_date
    FROM cte t1
)

SELECT
    (SELECT t2.id FROM yourTable t2 WHERE t2.date = t1.nearest_date) id,
    (SELECT t2.price FROM yourTable t2 WHERE t2.date = t1.nearest_date) price,
    t1.date
FROM cte2 t1
ORDER BY
    t1.date;

enter image description here

Demo

注意:要使此功能适用于8或更高版本的MySQL版本,您需要内联上述CTE。它将导致冗长的代码,但仍然可以使用。