简体   繁体   中英

sql query to fetch exchange rate

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

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

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 :

  1. The Exchange Rate table has conversion rates for a variety of target currencies, not just USD. The test data does not illustrate that, but in the query I select only rows where 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).
  2. The exchange rate column is not nullable. (It shouldn't be!)
  3. For each transaction date, for the same currency there is a row in the exchange rate table with the same or earlier effective start date. This is a cross-table constraint that should be maintained at the DB level.

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.

This should be easy to follow

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM