我有两张桌子 -
sales
和 currency_rate
。我想根据表 CurrRate
col Sales
和表 sales
col [Date]
中的月份,将 col currency_rate
填充到 CurrentPeriod
中的每个交易行。如果表中的日期链发生中断 currency_rate
我想将上个月的前一个 CurrRate
值填充到 Sales
的实际行。值得一提的是,CurrentPeriod
和PreviousPeriod
都是日期时间,表Date
中的Sales
是int,例如20240701
摘自
Sales
日期 | 价值 |
---|---|
20230404 | 1226.55 |
20230506 | 8.00 |
20230717 | 13834.73 |
20230916 | 10120.71 |
20230904 | 8.00 |
20230506 | 102.14 |
20230912 | 8.00 |
20230526 | 12813.40 |
20230805 | 779.90 |
来自
currency_rate
的片段(假设一个句号为空)
当前时期 | 上一期 | 当前汇率 |
---|---|---|
2023-04-01T00:00:00.0000000 | (空) | 0.894166 |
2023-05-01T00:00:00.0000000 | 2023-04-01T00:00:00.0000000 | 0.893192 |
2023-06-01T00:00:00.0000000 | 2023-05-01T00:00:00.0000000 | 0.879856 |
2023-07-01T00:00:00.0000000 | 2023-06-01T00:00:00.0000000 | 0.887729 |
(空) | 2023-07-01T00:00:00.0000000 | 0.896162 |
2023-09-01T00:00:00.0000000 | 2023-08-01T00:00:00.0000000 | 0.898093 |
预期结果:
日期 | 价值 | 当前汇率 |
---|---|---|
20230404 | 1226.55 | 0.894166 |
20230506 | 8.00 | 0.893192 |
20230717 | 13834.73 | 0.887729 |
20230916 | 10120.71 | 0.898093 |
20230904 | 8.00 | 0.898093 |
20230506 | 102.14 | 0.893192 |
20230912 | 8.00 | 0.898093 |
20230526 | 12813.40 | 0.893192 |
20230805 | 779.90 | 0.887729 |
如您所见,
20230805
的值是0.887729
,属于2023-07-01T00:00:00.0000000
,因为它是2023年08月的上一个时期
目前我所做的是使用
PreviousPeriod
函数将 currency_rate
转换为 LAG
。
我的一个疑问是 - LAG 应该按
CurrentPeriod
ASC 还是 DESC 排序?目前是 ASC,但我不知道是否应该是 DESC - 关于这个主题有什么想法吗?
如何使用spark.sql进行连接(可以在T-SQL中,我将其转换为spark),这样我就可以获得适当的当前汇率,即使我在日期中有NULL(不是预期的,但现在更好地覆盖它而不是返回当某事失败时)。为了涵盖我使用的连接的数据类型转换
date_format(rates.currentperiod, 'yyyyMM') = date_format(to_date(cast(sales.date as string), 'yyyyMMdd'), 'yyyyMM')
任何帮助将非常感激
我的询问
SELECT
inv.*
IFNULL(cr.CurrRate, 0) AS CurrencyRate
FROM
v_sales inv
LEFT JOIN (
SELECT
CurrentPeriod,
LAG(CurrentPeriod) OVER (PARTITION BY curr_rate_from, curr_rate_to ORDER BY CurrentRate) AS PreviousPeriod,
curr_rate_from,
curr_rate_to,
CurrRate
FROM
schema.currency_rate
) cr ON
inv.curr_code = cr.curr_rate_from -- not that important
AND (
(date_format(to_date(cast(inv.Day AS STRING), 'yyyyMMdd'), 'yyyyMM') = date_format(cr.CurrentPeriod, 'yyyyMM'))
OR
(date_format(to_date(cast(inv.Day AS STRING), 'yyyyMMdd'), 'yyyyMM') < date_format(cr.CurrentPeriod, 'yyyyMM')
AND date_format(to_date(cast(inv.Day AS STRING), 'yyyyMMdd'), 'yyyyMM') >= date_format(cr.PreviousPeriod, 'yyyyMM'))
)
我的
CurrencyRate
里还有一些0,不应该是这样的
一个选项是创建一个递归 cte 以生成销售表中从最小日期到最大日期的所有月份的日历。如果您正在处理日期,那么使用日期数据类型(而不是数字或字符串)是最有效的。在下面的答案中,所有日期列都被截断为一个月。
将销售和费率表连接到日历 cte,处理空值并获得预期结果:
WITH -- recursive cte generating months for period of sales
cal ( m_id, a_month, month_untill) AS
( Select 1 as m_id, month_from as a_month, month_untill
From ( Select Min(TRUNC(To_Date(dat, 'yyyyMMdd'), 'MM')) as month_from,
Max(TRUNC(To_Date(dat, 'yyyyMMdd'), 'MM')) as month_untill
From sales
)
UNION ALL
Select m_id + 1, ADD_MONTHS(a_month, 1), month_untill
From cal
Where a_month < month_untill
)
...这里的销售表是内连接 - 但如果您想查看期间的所有月份(有销售或没有销售),请将其更改为左连接。费率表已左连接到日历,如果没有当前期间日期,则使用 Coalesce() 函数获取前一个日期...
-- S Q L :
Select s.dat, s.val,
Coalesce(cr.rate, LAG(cr.rate) Over(Order By cal.a_month)) as rate
From cal
Inner Join sales s ON( Trunc(To_Date(SubStr(s.dat, 1, 10), 'yyyyMMdd'), 'MM') = cal.a_month )
Left Join currency_rate cr ON( Coalesce(Trunc(cr.currentperiod, 'MM'), Trunc(cr.previousperiod, 'MM')) = cal.a_month And
Trunc(cr.currentperiod, 'MM') = Trunc(To_Date(SubStr(s.dat, 1, 10), 'yyyyMMdd'), 'MM') )
Order By cal.a_month, s.dat
/* R e s u l t :
DAT VAL RATE
---------- ---------- ----------
20230404 1226.55 0.894166
20230506 8 0.893192
20230506 102.14 0.893192
20230526 12813.4 0.893192
20230717 13834.73 0.887729
20230805 779.9 0.887729
20230904 8 0.898093
20230912 8 0.898093
20230916 10120.71 0.898093 */