PostgreSQL-запрос курсов конвертации валют

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

fromCurrency toCurrency Дата вступления в силу коэффициент конверсии доллар США индийская рупия 1 марта 2024 г. 80 доллар США индийская рупия 1 января 2024 г. 85 доллар США Фунт стерлингов 1 октября 2023 г. .80 доллар США Фунт стерлингов 1 марта 2024 г. .85 доллар США австралийский доллар 1 марта 2023 г. 1,55 доллар США австралийский доллар 1 марта 2024 г. 1,60

Если к базе данных запрашивается 1 февраля 2024 г., чтобы получить коэффициент конверсии, где fromCurrency='USD', она должна вернуть

fromCurrency toCurrency Дата вступления в силу коэффициент конверсии доллар США индийская рупия 1 января 2024 г. 85 доллар США Фунт стерлингов 1 октября 2023 г. .80 доллар США австралийский доллар 1 марта 2023 г. 1,55

Тот же запрос 15 апреля 2024 г. должен вернуться.

fromCurrency toCurrency Дата вступления в силу коэффициент конверсии доллар США индийская рупия 1 марта 2024 г. 80 доллар США Фунт стерлингов 1 марта 2024 г. .85 доллар США австралийский доллар 1 марта 2024 г. 1,60

По сути, для всех возможных комбинаций (fromCurrency, toCurrency) в базе данных существует множество записей, и в определенную дату, когда к базе данных запрашивается, только одна из этих многих записей применима в зависимости от даты вступления в силу. Я пытаюсь найти наилучший запрос Postgres для получения записей.

Пожалуйста, поделитесь вопросом, который у вас есть на данный момент.

Janez Kuhar 26.04.2024 14:36

«Лучший возможный запрос Postgres» лучше всего в каком отношении? По каким критериям сравнивать возможные решения?

aled 26.04.2024 15:18

@aled извини, я виноват, я имел в виду производительность. В таблице очень большой объем данных.

Rax 26.04.2024 18:42
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
3
84
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Попробуйте следующий запрос PostgreSQL, чтобы получить применимые коэффициенты конверсии на основе даты запроса (или даты системы получения):

SELECT
  from_currency,
  to_currency,
  MAX(effective_date) FILTER (WHERE effective_date <= '2023-12-05') AS effective_date,
  MAX(conversion_rate) FILTER (WHERE effective_date <= '2023-12-05') AS conversion_rate
FROM conversion_rates
WHERE from_currency = 'USD'
GROUP BY from_currency, to_currency
ORDER BY from_currency, to_currency;

Я сохранил настраиваемые значения effect_date и from_currency, которые вы, возможно, захотите изменить (например, системную дату получения) по мере необходимости.

Моя Скрипка для этой игры.

Это может привести к получению значений из разных строк для effective_date и conversion_rate и, следовательно, к неверным результатам.

Lukas Eder 26.04.2024 14:56

Если вы хотите использовать этот подход, вы можете использовать (ARRAY_AGG(ROW(effective_date, conversion_rate) ORDER BY effective_date DESC) FILTER (WHERE effective_date <= '2024-02-01'))[1], как показано в этой обновленной скрипте: dbfiddle.uk/KmjLcXLS. К сожалению, в PostgreSQL нет поддержки MAX(ROW(...)).

Lukas Eder 26.04.2024 15:09
Ответ принят как подходящий

Как в jOOQ, так и в собственном PostgreSQL вы можете использовать DISTINCT ON, это предложение, в котором вы можете запросить только первую запись для каждого выражения DISTINCT ON, учитывая предложение ORDER BY:

С SQL:

SELECT DISTINCT ON (fromCurrency, toCurrency) 
  fromCurrency, toCurrency, effectiveDate, conversionRate
FROM conversions
WHERE effectiveDate <= :currentDate
ORDER BY fromCurrency, toCurrency, effectiveDate DESC

С помощью jOOQ (поскольку вы отметили вопрос jOOQ):

var c = CONVERSIONS.as("c");

ctx.select(c.FROM_CURRENCY, c.TO_CURRENCY, c.EFFECTIVE_DATE, c.CONVERSION_RATE)
   .distinctOn(c.FROM_CURRENCY, c.TO_CURRENCY)
   .from(c);
   .where(c.EFFECTIVE_DATE.le(currentDate))
   .orderBy(c.FROM_CURRENCY, c.TO_CURRENCY, c.EFFECTIVE_DATE.desc())
   .fetch();

Посмотрите на эту db-скрипку

Вот запрос, который может дать вам коэффициент конверсии на определенную системную дату. Если вы не хотите использовать системную дату, вам придется заменить current_date параметром.

WITH currList AS (
   SELECT *,
          max(effectiveDate) FILTER(WHERE effectiveDate <= CURRENT_DATE) OVER(PARTITION by fromCurrency, toCurrency) as maxEffDate
   FROM ConversionRates
)
SELECT *
FROM currList 
WHERE effectiveDate = maxEffDate

Другие вопросы по теме