具體需求是這樣,有一張旅館住宿表,表內有入住時間、離店時間等字段,現需要根據入住時間與離店時間生成入住期間的房晚數據。
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 ;