情况:我有汇率表,如下所示:
date_from cur1 coef1 cur2 coef2
2017-01-01 CZK 27.000000000 EUR 1.000000000
2017-07-03 EUR 1.000000000 CZK 26.150000000
2017-07-03 JPY 100.000000000 CZK 19.500000000
2017-10-05 JPY 1000.0000000 EUR 7.54761885
请注意,有时可以为同一对切换cur1
和cur2
。该表还包含其他货币对。两个系数的原因是手动填充表格(以便人类大脑更容易理解数字 - 请参阅JPY转换)。
然后我有另一张表格,其中包含发票行,其中价格以当地货币表示(即每行都有自己的货币单位靠近价格值)
我需要在发票行表格上执行一些SELECT
,并将价格转换为以所选目标货币显示 (比如,欧元中的一切)。如何有效地做到这一点?
我的第一次尝试:我事先了解目标货币。这意味着构建一个易于连接的简化结构的临时表应该更好。让目标货币为欧元。然后,将仅使用上表的子集,切换一些对,并且将两个系数转换为一个速率。目标货币将是固定或隐含的。从上表中,JPY-CZK对不会成为表的一部分:
date_from cur rate
2017-01-01 CZK 27.000000000
2017-07-03 CZK 26.150000000
2017-10-05 JPY 0.00754761885
要将行与另一个表连接,我不仅需要date_from
,还需要date_to
。为了能够在连接条件中使用BETWEEN
,我希望将date_to
作为下一个时段之前的date_from date_to cur rate
2017-01-01 2017-07-02 CZK 27.000000000
。对于CZK,我需要有一个像:
date_to
请注意下一个date_from
的{{1}}中的一天假。
但是,我还需要自动添加明确表示的时间间隔之前和之后的日期的一些边界值。我需要这样的东西:
date_from date_to cur rate
1900-01-01 2016-12-31 CZK 27.000000000 <-- guessed rate from the next; fixed start at the year 1900
2017-01-01 2017-07-02 CZK 27.000000000
2017-07-03 3000-01-01 CZK 26.150000000 <-- explicit rate; boundary year to 3000
同样适用于同一临时表中的其他货币...
1900-01-01 2017-10-04 JPY 0.00754761885 <-- rate guessed from the next; fictional date_from
2017-10-05 3000-01-01 JPY 0.00754761885 <-- explicit rate; fictional date_to
如何有效地构建这样的临时表?
您对此问题有任何其他建议吗?
更新:我已将我的解决方案发布到代码审核https://codereview.stackexchange.com/q/177517/16189请查看相关漏洞。
答案 0 :(得分:1)
我认为你不需要临时表。
首先,您需要获取每张发票中date_from
值最高的费率。这只是date_from
上的MAX,其中日期的限制小于发票的日期。对于示例,我使用CZK
作为转换为的货币:
SELECT
invoices.id
, invoices.cur
, MAX(date_from) AS current
FROM invoices
JOIN rates
ON rates.cur1 = invoices.cur
AND invoices.date > rates.date_from
AND rates.cur2 = 'CZK'
GROUP BY invoices.id, invoices.cur, invoices.date
由于GROUP BY导致SELECT可用列的限制,我们现在必须再次加入这两个表,然后加入它以获取当前速率:
SELECT
invoices.id
, invoices.cur
, invoices.amount
, 'CZK' AS otherCurrency
, invoices.amount / rates.coef1 * rates.coef2 AS converted
FROM invoices
JOIN
(SELECT
invoices.id
, invoices.cur
, MAX(date_from) AS current
FROM invoices
JOIN rates
ON rates.cur1 = invoices.cur
AND invoices.date > rates.date_from
AND rates.cur2 = 'CZK'
GROUP BY invoices.id, invoices.cur, invoices.date) AS current_rate
ON invoices.id = current_rate.id
JOIN rates
ON current_rate.current = rates.date_from
AND rates.cur1 = invoices.cur
AND rates.cur2 = 'CZK'
我准备了一个Tracking Issue for Material Chart Feature Parity来显示SQL的实际效果。
答案 1 :(得分:1)
假设汇率表如下,汇率为您的目标货币:
CREATE TABLE currency_rate (
currency_id INT NOT NULL,
update_date DATE NOT NULL,
rate DECIMAL(18,6) NOT NULL,
CONSTRAINT PK_currency_rate PRIMARY KEY(currency_id,update_date)
);
您可以使用相关子查询将发票链接到汇率:
SELECT
i.*,
cr.rate
FROM
invoice AS i
INNER JOIN currency_rate AS cr ON
cr.currency_id=i.currency_id AND
cr.update_date=(
SELECT
MAX(cr_i.update_date)
FROM
currency_rate AS cr_i
WHERE
cr_i.currency_id=i.currency_id AND
cr_i.update_date<=i.invoice_date
);
如果您确实有很多发票和大量费率,基于临时表的解决方案可能会提高性能。最好衡量哪一个获胜。基于相同的currency_rate
表定义:
CREATE TABLE #cr (
date_from DATETIME,
date_to DATETIME,
currency_id INT,
rate DECIMAL(18,6)
);
CREATE CLUSTERED INDEX IX_tcr_curr_dt ON #cr(currency_id,date_from);
INSERT INTO #cr (
date_from,
date_to,
currency_id,
rate
)
SELECT
date_from=ISNULL(DATEADD(DAY,1,LAG(update_date) OVER (PARTITION BY currency_id ORDER BY update_date)), '17530101'),
date_to=CASE WHEN LEAD(update_date) OVER (PARTITION BY currency_id ORDER BY update_date) IS NULL THEN '99991231' ELSE update_date END,
currency_id,
rate
FROM
currency_rate AS cr;
SELECT
i.*,
c.rate
FROM
invoices AS i
INNER JOIN #cr AS c ON
c.currency_id=i.currency_id AND
c.date_from<=i.invoice_date AND
c.date_to>=i.invoice_date;
DROP TABLE #cr;