Создать строку для каждого дня в диапазоне дат? - PullRequest
0 голосов
/ 12 февраля 2019

У меня есть такая таблица:

+----+---------+------------+
| 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 |
+----+---------+------------+

Я могу сделать это, используя PHP с циклом (foreach) и создав временную переменную, которая содержит предыдущую цену, пока не появится новая дата.

Но мне нужно сделать представление ... Так что я должен сделать это с использованием чистого SQL ... Есть идеи, как мне это сделать?

Ответы [ 3 ]

0 голосов
/ 12 февраля 2019

Вот подход, который должен работать без аналитических функций.В этом ответе используется подход соединения календарной таблицы.Первый CTE ниже - это базовая таблица, на которой основывается остальная часть запроса.Мы используем коррелированный подзапрос, чтобы найти самую последнюю дату раньше , чем текущая дата в CTE, цена которого не NULL.Это основание для выяснения того, какими должны быть значения id и price для тех дат, приходящих из календарной таблицы, которые не отображаются в исходном наборе данных.

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

Демонстрация

Примечание. Чтобы это работало на версиях MySQL более ранних, чем 8+, вам потребуется встроитьCTE выше.Это приведет к подробному коду, но все равно должно работать.

0 голосов
/ 13 февраля 2019

Поскольку вы используете 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 с последовательностью 'производная таблица' слева '.

Используйте что-то вроде вышеупомянутого в качестве производной таблицы в вашем запросе.

0 голосов
/ 12 февраля 2019

Вы можете использовать рекурсивный 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;

демо с mysql 8

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...