Как я могу рассчитать цены на основе таблицы валют за один выбор? - PullRequest
1 голос
/ 25 мая 2020

У меня есть таблица счетов-фактур, которые могут быть в нескольких валютах, которая выглядит следующим образом:

| id | issue_date | total | currency |
|----|------------|-------|----------|
| 1  | 2020-04-20 | 1234  | EUR      |
| 2  | 2020-04-26 | 2345  | USD      |
| 1  | 2020-04-27 | 9876  | EUR      |
| 3  | 2020-04-28 | 3456  | RON      |

И у меня есть таблица валют с курсами обмена, которая выглядит следующим образом:

| id | date       | currency_id | rate    |
|----|------------|-------------|---------|
| 1  | 2020-04-20 | EUR         | 1       |
| 2  | 2020-04-20 | USD         | 1.08600 |
| 3  | 2020-04-20 | RON         | 4.83560 |

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

Все курсы обмена валют основаны на евро, поэтому его стоимость всегда будет быть 1. Валюты обновляются ежедневно, но отсутствуют даты (в выходные дни обменные курсы не обновляются), поэтому расчет должен быть основан на самом последнем обменном курсе до invoice.issue_date

Итак, я попробовал следующее:

SELECT
    `i`.`id`,
    `i`.`total`,
    `i`.`currency`,
    `exr1`.`rate` as `invoice_rate`,
    `exr2`.`rate` AS `target_rate`,
    `i`.`total` * `exr1`.`rate` as `euro_price`,
    `i`.`total` * `exr1`.`rate` / `exr2`.`rate` AS `target_price`
FROM `invoices` as `i`

LEFT JOIN `exchange_rates` AS `exr1`
ON
    `exr1`.`currency_id` = `i`.`currency` AND
    `exr1`.`date` = `i`.`issue_date`

LEFT JOIN `exchange_rates` as `exr2`
ON
    `exr2`.`currency_id` = 'RON' AND
    `exr2`.`date` = `i`.`issue_date`

GROUP BY
    `i`.`id`,
    `invoice_rate`,
    `target_rate`

ORDER BY `i`.`issue_date` DESC

Проблема № 1

Из-за отсутствия ставок обмена на точные даты выставления счетов я получаю значения null. Я попытался изменить LEFT JOIN ON на что-то вроде exr1.date <= i.issue_date, но счет GROUP BY больше не работает (я получаю дубликаты).

Проблема № 2

Для строк с курсами обмена в тот же день я получаю неправильные значения, потому что в зависимости от целевой валюты мне нужно либо умножить, либо разделить: i.total * exr1.rate * exr2.rate AS usd_price vs i.total * exr1.rate / exr2.rate AS usd_price

https://www.db-fiddle.com/f/e5GnVnry5sAiXwbuScV6JT/19

1 Ответ

0 голосов
/ 25 мая 2020

Это (редкий) случай, когда зависимый подзапрос - это путь к go. Вот общий запрос (https://www.db-fiddle.com/f/e5GnVnry5sAiXwbuScV6JT/21)

SELECT id, 
       total,
       currency,
       rate,
       total / rate euro_price
  FROM (  SELECT i.id,
                 i.total,
                 i.currency,
                 (SELECT e.rate
                    FROM exchange_rates e
                   WHERE e.currency_id = i.currency
                     AND e.date <= i.issue_date
                   ORDER BY e.date DESC
                   LIMIT 1) rate
              FROM invoices i
        ) d

Зависимый подзапрос:

                  SELECT e.rate
                    FROM exchange_rates e
                   WHERE e.currency_id = i.currency
                     AND e.date <= i.issue_date
                   ORDER BY e.date DESC
                   LIMIT 1

Он находит, что обменный курс на последнюю дату равен до или до issue_date. Он называется зависимым , потому что он ссылается на значения столбцов во внешнем запросе.

Это будет не быстро. Индекс покрытия на exchange_rates(currency_id, date DESC, rate) поможет. Вот так.

CREATE INDEX lookup ON exchange_rates(currency_id, date DESC, rate);

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

Также обратите внимание, что я думаю, вы хотите разделить, а не умножить, при вычислении вашей euro_price.

Я оставил вам второй поиск.

** Совет для профессионалов * Используйте обратные кавычки, только если имя вашей таблицы или столбца является зарезервированным словом в языке запросов. Ваши запросы НАМНОГО легче читать без них.

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