Reputation: 43
Given a sales table with sales amount stored in local currencies and an exchange rate table containing currency conversion rate, to get total sales amount in USD for each sales date I need query
Sales Table:
Sales Date Sales Amount Currency
01-JAN-16 500 INR
01-JAN-16 100 GBP
02-JAN-16 1000 INR
02-JAN-16 150 GBP
03-JAN-16 1500 INR
Exchange Rate Table:
Source Currency Target Currency Exchange Rate Effective Start Date
INR USD 0.014 31-DEC-15
INR USD 0.015 02-JAN-16
GBP USD 1.32 20-DEC-15
GBP USD 1.30 01-JAN-16
GBP USD 1.35 10-JAN-16
have no idea how I should proceed
I have to do two things match the currency and then check for the same date exchange rate or before sales_date
Upvotes: 0
Views: 14946
Reputation: 1
with ex as
(
select e.source_currency, e.exchange_rate, e.effective_start_date,
isnull(lead(dateadd(day, -1,cast(effective_start_date as date))) over(partition by source_currency order by effective_start_date ),cast('9999-12-31' as date)) eff_end_date
from exchange_rate e
)
select s.sales_date, sum(round((s.sales_amount * e.exchange_rate),2)) amt
from ex e
join sales_amount s
on e.source_currency = s.currency and
s.sales_date between e.effective_start_date and e.eff_end_date
group by s.sales_date
order by s.sales_date
Upvotes: 0
Reputation:
Long ago Oracle introduced analytic functions for this kind of processing - to avoid joins, which may often be expensive (take a long time to process, compared to everything else in the query).
In this type of problem, it is most efficient to union all
the two tables, using null
column values where needed; then use the last_value
function, ignoring nulls, and then just collect the results.
Assumptions:
target_currency = 'USD'
to allow for that. (Indeed, if the table only had exchange rates for target = USD then the target currency column wouldn't be needed).Query (including test data in with
clause - not needed when using base tables)
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
;
Output:
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.
Note: The query output has each column in its proper data type (date, number etc.). The formatting was all done in SQL*Plus; we don't want to format the data (convert it to strings) in the SQL query, since perhaps this is not the final product; its output may be consumed by further processing.
Upvotes: 0
Reputation: 11195
Try this:
with SOURCE as
(
select s1.*, coalesce(e1.Rate, 1) as ExRate, row_number() over(partition by e2.Source order by e2.StartDate desc) as r_num
from Sales s1
left join Exchange e2
on s1.Currency = e2.Source
and e2.StartDate <= s1.SalesDate
)
select SOURCE.*, SalesAmount*ExRate as USDAmount
from SOURCE
where r_num = 1
Upvotes: 1