Как объединить две таблицы с отсутствующими строками за последний месяц?

#sql #r #postgresql #window-functions

#sql #r #postgresql #окно-функции

Вопрос:

Я хочу объединить две таблицы, и в одной отсутствует строка для каждой валютной группы.

Таблицы:

Транзакции в местной валюте, агрегированные по месяцам. (таблица транзакций)

 Date            Currency       spend
2019-01-01       EUR             100
2019-02-01       EUR             200 
2019-03-01       EUR             500
2019-04-01       EUR             214
2019-01-01       JYP            3200
2019-01-01       JYP            1534
2019-02-01       JYP            1534
2019-03-01       JYP            1534
2019-04-01       JYP            1534
  

Обменный курс по месяцам (таблица exchange_data)

 Month            Currency       Average Monthly rate
2019-01-01       EUR            1.2
2019-02-01       EUR            1.3 
2019-03-01       EUR            1.4
2019-01-01       JYP            101
2019-02-01       JYP            102
2019-03-01       JYP            103
2019-01-01       USA            1
2019-02-01       USA            1
2019-03-01       USA            1
  

Я хочу выполнить объединение, чтобы получить все транзакции в долларах США. Проблема в том, что скорость за текущий месяц (2019-04-01) недоступна. Таким образом, все транзакции за текущий месяц возвращают NULL после объединения.

Мне удалось решить эту проблему в R, но есть ли способ решить ее с помощью SQL? Я пытался использовать функцию окна, но безуспешно

 LAG(rate,1) OVER (PARTITION BY currency ORDER BY month)
  

Решение в R: предполагая, что скорость остается постоянной.

 library(lubridate)
library(dplyr)

exchange_previous <- exchange_data[exchange_data$month == floor_date(today(),"month") %m-% months(1),]
exchange_previous$month <- exchange_previous$month %m % months(1)
exchange_data<-rbind(exchange_data,exchange_previous)

final <- transactions %>%
left_join(exchange_data, by = c("currency" = "name", "floor_date" = "month"))
Then simply multiply
  

Комментарии:

1. @GordonLinoff postgresql

2. Пожалуйста, опубликуйте желаемые результаты или выходные данные вашего R-кода для тех пользователей Postgres, которые не используют R.

3. Все ли JPP должны быть JYP? Если нет обменного курса, будете ли вы использовать курс предыдущего месяца?

4. @xavier да, я виноват. JPP — это JYP. Да, за предыдущий месяц

Ответ №1:

Используйте боковое соединение, но оно должно выглядеть так:

 select t.*, ed.average_monthly_rate,
from transactions t left join lateral
     (select ed.*
      from exchange_data ed
      where ed.currency = t.currency and
            ed.month <= t.date
      order by ed.month desc
      fetch first 1 row only
     ) ed
     on 1=1;
  

Я не уверен, хотите ли вы разделить или умножить на скорость.

Комментарии:

1. Хорошо сыграно, я не думал о ORDER BY month DESC LIMIT 1 боковом соединении.

Ответ №2:

Вы можете использовать a LATERAL JOIN для применения самого последнего обменного курса для валюты, например:

         SELECT tr.*, rates.rate
          FROM transactions tr
          LEFT JOIN
       LATERAL (SELECT rate
                     -- order rows by date, most recent first
                     , ROW_NUMBER() OVER (PARTITION BY currency 
                                              ORDER BY d DESC) rn 
                  FROM exchange_data 
                 WHERE currency = tr.currency 
                   -- exchange dates only up until the month of a transaction
                   AND d <= tr.d
             ) rates
            ON rates.rn = 1 -- row for the most recent date
  

LATERAL JOIN аналогично APPLY SQL Server, поскольку позволяет присоединять результирующий набор с использованием предикатов из внешнего запроса.

Вот рабочий пример на dbfiddle.

Комментарии:

1. Ответ Гордона using ORDER BY month DESC LIMIT 1 лучше.

Ответ №3:

Предполагая, что ваши таблицы называются Transactions и Exchange, следующий код работает для MySQL. Я думаю, Postgresql будет похож.

 select C.date, C.spend/E.Average 
   from Transactions as C, Exchange as E,
      (select A.date, A.Currency, Max(B.Month) as Month 
         from Transactions as A, Exchange as B
         where A.Currency = B.Currency and A.datum >= B.Month
         group by A.date, A.Currency
      ) as D
where C.date = D.date 
   and C.Currency = D.Currency 
   and E.Month = D.Month 
   and C.Currency = E.Currency
order by C.date;
  

Идея, лежащая в его основе, заключается в следующем: учитывая дату и валюту, вы просматриваете таблицу D, которая является ближайшей прошедшей датой для этой валюты. Как только у вас это будет, вы сможете получить обмен, который вы искали.