давно Oracle представила аналитические функции для этого вида обработки - чтобы избежать объединения, что часто может быть дорогостоящим (требуется много времени для обработки, по сравнению со всем остальным в запросе).
В этом типе проблемы наиболее эффективны union all
две таблицы, используя значения столбцов null
, где необходимо; затем используйте функцию last_value
, игнорируя нули, а затем просто собирайте результаты.
Предположения:
- таблица обменных курсов имеет коэффициент конверсии для различных целевых валют, а не только USD. Данные теста не иллюстрируют это, но в запросе я выбираю только строки, где
target_currency = 'USD'
, чтобы это разрешить. (Действительно, если в таблице только обменные курсы для целевого = доллара, то целевой столбец валюты не понадобится).
- Курс обмена не может быть обнулен. (Не должно быть!)
- Для каждой даты транзакции для той же валюты есть строка в таблице обменных курсов с той же или более ранней эффективной датой начала. Это ограничение между таблицами, которое должно поддерживаться на уровне БД.
Запрос (в том числе тестовых данных в with
пункте - не нужны при использовании базовых таблиц)
with
sales (sales_date, sales_amount, currency) as (
select to_date('01-JAN-16', 'dd-MON-rr'), 500, 'INR' from dual union all
select to_date('01-JAN-16', 'dd-MON-rr'), 100, 'GBP' from dual union all
select to_date('02-JAN-16', 'dd-MON-rr'), 1000, 'INR' from dual union all
select to_date('02-JAN-16', 'dd-MON-rr'), 150, 'GBP' from dual union all
select to_date('03-JAN-16', 'dd-MON-rr'), 1500, 'INR' from dual
),
exch_rate (source_currency, target_currency, exchange_rate, effective_date) as (
select 'INR', 'USD', 0.014, to_date('31-DEC-15', 'dd-MON-rr') from dual union all
select 'INR', 'USD', 0.015, to_date('02-JAN-16', 'dd-MON-rr') from dual union all
select 'GBP', 'USD', 1.32, to_date('20-DEC-15', 'dd-MON-rr') from dual union all
select 'GBP', 'USD', 1.30, to_date('01-JAN-16', 'dd-MON-rr') from dual union all
select 'GBP', 'USD', 1.35, to_date('10-JAN-16', 'dd-MON-rr') from dual
),
prep (dt, amt, src_curr, x_rate) as (
select sales_date, sales_amount, currency, null from sales
union all
select effective_date, null, source_currency, exchange_rate
from exch_rate
where target_currency = 'USD'
),
with_x_rates (dt, amt, src_curr, x_rate) as (
select dt, amt, src_curr,
last_value (x_rate ignore nulls)
over (partition by src_curr order by dt, x_rate) as x_rate
from prep
)
select dt as sales_date, amt as sales_amount, src_curr as currency,
x_rate as exchange_rate,
amt * x_rate as sales_amount_in_usd
from with_x_rates
where amt is not null
order by sales_date, currency -- if needed
;
Выходные:
SALES_DATE SALES_AMOUNT CURRENCY EXCHANGE_RATE SALES_AMOUNT_IN_USD
---------- ------------ -------- ------------- -------------------
01-JAN-16 100 GBP 1.300 130.000
01-JAN-16 500 INR 0.014 7.000
02-JAN-16 150 GBP 1.300 195.000
02-JAN-16 1000 INR 0.015 15.000
03-JAN-16 1500 INR 0.015 22.500
5 rows selected.
Примечание: Выходной сигнал запроса имеет каждый столбец в соответствующем типе данных (дата, номер и т. д.). Форматирование было выполнено в SQL * Plus; мы не хотим форматировать данные (конвертировать их в строки) в SQL-запрос, поскольку, возможно, это не конечный продукт; его выход может потребляться дальнейшей обработкой.
спасибо, я могу понять и его запуск –