SQL добавить пропущенные даты от самой последней до текущей даты - PullRequest
0 голосов
/ 25 апреля 2020

У меня есть таблица валют, которая заполняется ежемесячно. Итак, 3-го числа каждого месяца данные за последний месяц обновляются.

Все, что я хочу - это иметь записи с самой последней даты до текущей даты. Если последняя запись для указанной комбинации c была найдена 1 февраля 2020 года, мне нужно заполнить таблицу валют таким образом, чтобы для этой комбинации были записи с 1 февраля по текущую дату. В основном это комбинация - тип валюты, Из валюты и в валюту.

Я знаю, что ключ Lead () является ключом к этому, но я не могу понять, как именно его использовать.

Ниже приведен небольшой пример:

Существующие записи :

timestamp type fcurr tcurr Conv_ratio
20200101   M    EUR   USD    0.9
20200201   M    EUR   USD    0.85
20200229   C    INR   EUR    0.4

Ожидаемый вывод:

timestamp type fcurr tcurr Conv_ratio
20200101   M    EUR   USD    0.9
20200201   M    EUR   USD    0.85
20200202   M    EUR   USD    0.85
20200203   M    EUR   USD    0.85
.
.
.
20200424   M    EUR   USD    0.85
20200229   C    INR   EUR    0.4
20200301   C    INR   EUR    0.4
20200302   C    INR   EUR    0.4
.
.
20200424   C    INR   EUR    0.4

Идеальный вывод Я смотрю, как показано ниже, но даже если я не могу получить это, вышеприведенный вывод мне подходит.

timestamp type fcurr tcurr Conv_ratio
20200101   M    EUR   USD    0.9
20200201   M    EUR   USD    0.85
20200301   M    EUR   USD    0.85
20200401   M    EUR   USD    0.85
20200229   C    INR   EUR    0.4
20200331   C    INR   EUR    0.4

В основном существуют записи на 1-е число каждого месяца для типа M и последние на каждый месяц для типа C.

Ответы [ 2 ]

1 голос
/ 25 апреля 2020

Я предположил, что есть только 2 валюты, но, кажется, их может быть больше. Поэтому я обновил свой запрос для обработки всех валют. Вот мой пример таблицы:

create or replace table currencies (ctimestamp varchar, ctype varchar, 
fcurr varchar, tcurr varchar, conv_ration number(3,2) )
as select * from values  
('20200101' ,  'M' , 'EUR' , 'USD'  ,  0.9),
('20200201' ,  'M' , 'EUR' ,  'USD' ,   0.85),
('20200229' ,  'C' , 'INR' ,  'EUR' ,   0.4);

Вот мое решение, которое не использует никаких временных таблиц или хранимых процедур, а просто SQL:

with generated_days as 
(select to_varchar( dateadd( days, row_number() over (order by 1) - 1, (select min(to_date(ctimestamp,'YYYYMMDD')) from currencies)), 'YYYYMMDD' ) gtimestamp 
from table(generator(rowcount => 10000))
),
first_last_days as
(select gtimestamp, IFF(substring(gtimestamp, -2 ) = '01', 'M', 'C' ) mc
from generated_days where ( substring(gtimestamp, -2 ) = '01' or last_day( to_date(gtimestamp,'YYYYMMDD')) = to_date(gtimestamp,'YYYYMMDD'))   
and to_date(gtimestamp,'YYYYMMDD') <= current_date
),
curtypes as 
(select ctype || fcurr || tcurr pkey, ctype, max(ctimestamp) max_date from currencies group by ctype || fcurr || tcurr, ctype),
combination as (
select pkey, gtimestamp, null ctype, null fcurr, null tcurr, null conv_ration
from first_last_days
join curtypes on gtimestamp > max_date and ctype = mc 
union all
select ctype || fcurr || tcurr pkey, * from currencies
order by pkey, gtimestamp)
select  
gtimestamp, left( pkey, 1 ) ctype,
NVL(fcurr, lag( fcurr, 1, NULL ) ignore nulls over (partition by pkey order by gtimestamp )) fcurr,
NVL(tcurr, lag( tcurr, 1, NULL ) ignore nulls over (partition by pkey order by gtimestamp )) tcurr,
NVL(conv_ration, lag( conv_ration, 1, NULL ) ignore nulls over (partition by pkey order by gtimestamp)) conv_ration
from combination
order by ctype, fcurr, tcurr, gtimestamp;

Сначала я генерирую даты, затем выберите только первый и последний дни каждого месяца. Я группирую существующие данные по каждой валюте и типу. Я присоединяюсь к этим группам с доступными датами и объединяюсь с существующими значениями. Я заполняю пропущенные значения с помощью функции LAG.

Вот результат вышеприведенного запроса:

+------------+-------+-------+-------+-------------+
| GTIMESTAMP | CTYPE | FCURR | TCURR | CONV_RATION |
+------------+-------+-------+-------+-------------+
|   20200229 | C     | INR   | EUR   |        0.40 |
|   20200331 | C     | INR   | EUR   |        0.40 |
|   20200101 | M     | EUR   | USD   |        0.90 |
|   20200201 | M     | EUR   | USD   |        0.85 |
|   20200301 | M     | EUR   | USD   |        0.85 |
|   20200401 | M     | EUR   | USD   |        0.85 |
+------------+-------+-------+-------+-------------+
0 голосов
/ 25 апреля 2020

Предполагая, что в вашей таблице есть столбец идентификатора и заказано, что решение будет работать в MySQL с одним запросом.

https://www.db-fiddle.com/f/owwJs6APbzncw9Vq9RzzTM/0

WITH  just_a_big_table AS (
SELECT t1.*,@x:=-1
FROM
    (
        SELECT 1 AS Number 
        UNION ALL SELECT 2
        UNION ALL SELECT 3
        UNION ALL SELECT 4
        UNION ALL SELECT 5
        UNION ALL SELECT 6
        UNION ALL SELECT 7
        UNION ALL SELECT 8
        UNION ALL SELECT 9
        UNION ALL SELECT 10
    ) AS t1
    CROSS JOIN (
        SELECT 1 AS Number 
        UNION ALL SELECT 2
        UNION ALL SELECT 3
        UNION ALL SELECT 4
        UNION ALL SELECT 5
        UNION ALL SELECT 6
        UNION ALL SELECT 7
        UNION ALL SELECT 8
        UNION ALL SELECT 9
        UNION ALL SELECT 10
    ) AS t2
    CROSS JOIN (
        SELECT 1 AS Number 
        UNION ALL SELECT 2
        UNION ALL SELECT 3
        UNION ALL SELECT 4
        UNION ALL SELECT 5
        UNION ALL SELECT 6
        UNION ALL SELECT 7
        UNION ALL SELECT 8
        UNION ALL SELECT 9
        UNION ALL SELECT 10
    ) AS t3

),
all_dates AS (SELECT date_format(DATE_SUB(NOW(), INTERVAL @x:=(@x+1) DAY),'%Y%m%d') dt FROM just_a_big_table),
min_max_dates AS(
     SELECT MIN(dt) AS min_date,MAX(dt) AS max_date FROM (  
     SELECT *,LEFT(dt,6) AS month FROM all_dates
     ) t GROUP BY month )
SELECT * FROM (
SELECT IF(type='M',m.min_date,m.max_date)AS timestamp,d.type,d.fcurr,d.tcurr,d.Conv_ratio FROM `min_max_dates` m INNER JOIN `data` d 
ON (m.min_date=d.timestamp AND d.type='M')
OR (m.max_date=d.timestamp AND d.type='C')
OR (m.min_date>d.timestamp AND d.type='M' AND d.id IN (SELECT MAX(id) FROM `data` GROUP BY type,fcurr,tcurr)) 
OR (m.max_date>d.timestamp AND d.type='C' AND d.id IN (SELECT MAX(id) FROM `data` GROUP BY type,fcurr,tcurr)) 
ORDER BY Conv_ratio DESC,timestamp ASC ) t WHERE timestamp!=date_format(NOW(),'%Y%m%d');

Сначала я создаю фиктивную таблицу из 10000 строк.

Затем заполняю ее датами в том формате, который у вас есть в таблице, начиная с текущей даты, каждый раз вычитая один день.

Затем я продолжаю только первый и последний день каждого месяца.

В конце я присоединяю эту таблицу к таблице данных (удаляя все строки текущей даты, которые были созданы, поскольку текущая дата является максимальной датой для этого месяца)

...