具体需求是这样,有一张旅馆住宿表,表内有入住时间、离店时间等字段,现需要根据入住时间与离店时间生成入住期间的房晚数据。
PS:实验环境为maxcompute,hive的同学请自行调整 (杂乱的命名请忽略= =)
住宿测试表(test_get_dates):
数据:
需要生产的房晚数据:
在大佬的鞭策下一共想了三种实现方案:
1)mapjoin日期码表,使用不等值关联生成
2)编写UDTF,传入开始与结束日期生成
3)使用函数 LATERAL VIEW explode生成
具体实现如下:
(一)生成一张日期码表,并使用mapjoin的不等值关联
1、创建时间码表dim_dates,如图
(id可以省略)
2、使用mapjoin关联码表生成所需数据
select /*+MAPJOIN(a) */ a.times, b.rzsj, b.tfsj, COALESCE(b.tfsj,b.rzsj) as bftfrq, --关联使用的住房日期 b.sfzhm from dim_dates a join test_get_dates b on a.times>=TO_DATE(to_char(b.rzsj,'yyyy-mm-dd'),'yyyy-mm-dd') and a.times<= TO_DATE(to_char(b.tfsj,'yyyy-mm-dd'),'yyyy-mm-dd')
查询结果如下
(二)编写UDTF,返回多行日期数据
1、编写UDTF,代码如下
import com.aliyun.odps.udf.UDTF; import com.aliyun.odps.udf.annotation.Resolve; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @Resolve({"datetime,datetime,string,string->datetime,string,string,datetime,datetime"}) public class GetDatesUDTF extends UDTF { /** * @param args rzrq,tfrq,dwd_zjid,zjhm * args[0] 入住日期 * args[1] 退房日期 * args[2] dwdid * args[3] 身份证号码 * @return 房晚日期,dwdid,身份证号码,入住时间,退房时间 */ @Override public void process(Object[] args) { Date bDate = (Date) args[0]; //获取入住时间 Date eDate = (Date) args[1]; //获取退房时间 Date bfDate = formateDate(bDate); Date efDate = formateDate(bDate!=null&&eDate==null?bDate:eDate); String dwdId = (String) args[2]; String sfzhm = (String) args[3]; if (bDate != null && efDate != null) { int days = getDateDiff(bfDate, efDate); try { Date d; for (int i = 0; i < days; i++) { d = formateDate(getAddDate(bfDate,i)); forward(d, dwdId,sfzhm,bDate,eDate); } } catch (Exception e) { e.printStackTrace(); } } } public Date getAddDate(Date date,int days){ Calendar calendar = new GregorianCalendar(); calendar.setTime(formateDate(date)); calendar.add(Calendar.DAY_OF_MONTH, days); return calendar.getTime(); } public Date formateDate(Date date) { if (date != null) { try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String fdateStr = sdf.format(date); date = sdf.parse(fdateStr); return date; } catch (Exception e) { e.printStackTrace(); } } return null; } public Date getEndDate(Date sdate, Date edate) { if (sdate != null && edate == null) { return formateDate(sdate); } return edate; } public int getDateDiff(Date sdate, Date edate) { long days = (edate.getTime() - sdate.getTime()) / (1000 * 60 * 60 * 24); return new Long(days).intValue(); } }
2、打包上传至maxcompute、创建函数getdates
3、使用UDTF
(三) 使用函数 LATERAL VIEW explode
select tf.*,t.*, dateadd(start_date,pos,'dd')
from (
select 'a' as a, '2018-11-01 00:00:00' as start_date, '2018-12-01 00:00:00' as end_date from test_get_dates limit 1
) t
lateral view posexplode(split(space(datediff(end_date,start_date,'dd')),' ')) tf as pos,val ;