我有三个表:monthly_revenue
,currencies
和foreign_exchange
。
|------------------------------------------------------|
| id | product_id | currency_id | value | month | year |
|------------------------------------------------------|
| 1 | 1 | 1 | 100 | 1 | 2015 |
| 2 | 1 | 2 | 125 | 1 | 2015 |
| 3 | 1 | 3 | 115 | 1 | 2015 |
| 4 | 1 | 1 | 100 | 2 | 2015 |
| 5 | 1 | 2 | 125 | 2 | 2015 |
| 6 | 1 | 3 | 115 | 2 | 2015 |
|------------------------------------------------------|
|---------------------------------------|
| id | base | target | rate | rate_date |
|---------------------------------------|
| 1 | GBP | USD | 1.6 |2015-01-01 |
| 2 | GBP | USD | 1.62 |2015-01-15 |
| 3 | GBP | USD | 1.61 |2015-01-31 |
| 4 | EUR | USD | 1.2 |2015-01-01 |
| 5 | EUR | USD | 1.4 |2015-01-15 |
| 6 | EUR | USD | 1.4 |2015-01-31 |
| 7 | GBP | EUR | 1.4 |2015-01-01 |
| 8 | GBP | EUR | 1.45 |2015-01-15 |
| 9 | GBP | EUR | 1.44 |2015-01-31 |
|---------------------------------------|
由此,我们可以看到平均fx率:
没有美元作为基础货币的汇率,2月份没有汇率。
|-----------|
| id | name |
|-----------|
| 1 | GBP |
| 2 | USD |
| 3 | EUR |
|-----------|
monthly_revenue
表格中的每一行都可以有不同的currency_id
,因为下达的订单是不同的货币。我想以一种共同货币查看给定月份的所有收入。因此,我不想一月份以英镑计算1月份的所有收入,而是单独查看1月份的所有收入,我希望1月份所有收入都能获得一个价值 - 转换为美元(例如)。
可以使用以下内容(对于此示例使用1月)计算每一行:
收入值 x 1月基本货币与目标货币之间的平均汇率
如果我在1月份有5个订单,有4种不同的货币,这让我看到所有单一货币的收入。
这应该返回:
|------------------------------------------------------|
| id | product_id | currency_id | value | month | year |
|------------------------------------------------------|
| 1 | 1 | 1 | 100 | 1 | 2015 |
| 2 | 1 | 2 | 125 | 1 | 2015 |
| 3 | 1 | 3 | 115 | 1 | 2015 |
|------------------------------------------------------|
但是,第1行和第3行不是美元(分别是GBP和EUR)。
我希望看到的每一行都返回了正在转换为的平均汇率,以及converted
列。例如:
|-------------------------------------------------------------------------|
| id | prod_id | currency_id | value | month | year | fx_avg | converted |
|-------------------------------------------------------------------------|
| 1 | 1 | 1 | 100 | 1 | 2015 | 1.61 | 161 |
| 2 | 1 | 2 | 125 | 1 | 2015 | 1 | 125 |
| 3 | 1 | 3 | 115 | 1 | 2015 | 1.33 | 152.95 |
|-------------------------------------------------------------------------|
我目前可以使用下面的查询完成基本计算,但缺少一些关键功能:
如果没有可用的外汇汇率(例如,对于未来日期,当然外汇汇率不可用)则忽略整行。在这种情况下,我喜欢的是使用最近一个月的平均值。
如果正在执行目标货币与基础货币相同的计算,则忽略整行(因为FX表中没有基数等于目标的记录)。在这种情况下,费率应该硬定义为1.
SELECT
r.value * IFNULL(AVG(fx.rate),1) as converted, AVG(fx.rate) as averageFx,
r.*, fx.*
FROM
foreign_exchange fx, monthly_revenue r, order_headers h
WHERE
fx.base IN (SELECT name FROM currencies WHERE id = r.currency_id) AND
r.order_header_id = h.id AND
fx.target = 'USD' AND
MONTH(fx.rate_date) = r.month AND
YEAR(fx.rate_date) = r.year AND
r.year = 2015
GROUP BY r.id
ORDER BY month ASC
如果FX没有可用的记录,则应该执行单独的子查询以获得最近一个月的平均费率。
任何输入都将不胜感激。如果需要进一步的信息,请发表评论。
感谢。
编辑 Here is a SQFiddle,其中包含示例模式和突出显示问题的代码。
答案 0 :(得分:6)
以下是计算特定货币和月初的交易所的函数的近似值:
DELIMITER //
CREATE FUNCTION MonthRate(IN _curr CHAR(3) CHARACTER SET ascii,
IN _date DATE)
RETURNS FLOAT
DETERMINISTIC
BEGIN
-- Note: _date must be the first of some month, such as '2015-02-01'
DECLARE _avg FLOAT;
DECLARE _prev FLOAT;
-- First, try to get the average for the month:
SELECT AVG(rate) INTO _avg FROM foreign_exchange
WHERE base = _curr
AND target = 'USD'
AND rate_date >= _date
AND rate_date < _date + INTERVAL 1 MONTH;
IF _avg IS NOT NULL THEN
RETURN _avg;
END;
-- Fall back onto the last rate before the month:
SELECT rate INTO _prev
FROM foreign_exchange
WHERE base = _curr
AND target = 'USD'
AND rate_date < _date
ORDER BY _date
LIMIT 1;
IF _prev IS NOT NULL THEN
RETURN _prev;
END;
SELECT "Could not get value -- ran off start of Rates table";
END;
DELIMITER ;
可能存在语法错误等。但希望您可以使用它。
从其余代码调用该函数应该很容易。
为了表现,这将是有益的:
INDEX(base, target, rate_date, rate)
答案 1 :(得分:0)
创建视图:
create view avg_rate as
select base, target, year(fx.rate_date) year, month(fx.rate_date) month,
avg(rate) avg_rate
from foreign_exchange group by base, target
加入两次,一次为当月,一次为前一次
select r.id, r.month,
r.value * avg(coalesce(cr.avg_rate, pr.avg_rate, 1)) converted,
avg(coalesce(cr.avg_rate, pr.avg_rate), 0) rate
from monthly_revenue r, avg_rate cr, avg_rate pr, order_headers h
where
r.year = 2015 and
cr.year = r.year and cr.month = r.month and cr.target='USD' and
pr.year = r.year and pr.month = r.month - 1 and pr.target='USD' and
r.order_header_id = h.id
group by r.id
order by r.month
此外,我个人不喜欢这种编写查询的方式,并且喜欢使用显式连接,因为您在逻辑上对条件进行分组并且在where子句中没有混乱。即:
...
from monthly_revenue r
inner join order_headers h on r.order_header_id = h.id
left join avg_rate cr on cr.year = r.year and cr.month = r.month and cr.target='USD'
left join avg_rate pr on pr.year = r.year and pr.month = r.month - 1 and pr.target='USD'
where r.year = 2015
答案 2 :(得分:0)
http://sqlfiddle.com/#!9/6a41a/1
这个小提琴基于你原来的小提琴,但我在2月和3月添加了一些费率值来测试并展示它是如何工作的。
WHERE
如果您需要特定月份的记录,可以在ORDER
之前添加WHERE r.month = 1 and r.year = 2015
ORDER BY r.id, fx.avg_date DESC) t
,如下所示:
.row-fluid
答案 3 :(得分:0)
您可以在您提供的小提琴链接上测试此查询:http://sqlfiddle.com/#!9/33def/2
select id,product_id,currency_id,currency_name,
value,month,year,@prev_fx_avg:=ifnull(fx_avg,@prev_fx_avg) fx_avg,
value*@prev_fx_avg as converted
from (SELECT
r.id,r.product_id,r.currency_id,c.name as currency_name,
r.value,r.month,r.year,if(c.name="USD",1,temp.avg_rate) as fx_avg
FROM
monthly_revenue r
left join currencies c on r.currency_id=c.id
left join
(select base , avg(rate) as avg_rate, MONTH(fx.rate_date) month,
YEAR(fx.rate_date) year
from foreign_exchange fx
where target="USD"
group by base,target,MONTH(fx.rate_date),
YEAR(fx.rate_date)) temp on(r.month=temp.month and r.year=temp.year and c.name=temp.base)
group by r.id
order by r.currency_id,r.month ASC, r.year ASC) final,(select @prev_fx_avg:=-1) temp2;