笛卡尔积妙用(跨月的数据处理)

场景数据比较多,主要摘出实现的条件字段做个例子记录:测试环境MYSQL8.0

数据表:

       业务场景:有这样的申请单,计算延迟单比率,按年月展示延迟单比率。

       现在需要按月统计单子当月的情况:在单子时长>=80个小时,则当月延迟单+1,计算每个月的延迟单率。

分析:看表我们可以看到数据有三中状态

     1、当月开始,当月结束;

     2、当月开始,下月(=1)结束;

     3、当月开始,跨月(>1)结束。

     如何判断是否属于延迟单:开始时间以及结束时间之差>=80h;

                            计算比率:当月延迟单数/当月总数。

      结论:此时我们需要明确的最简易的算法就是,得到每个月每一条数据的开始时间与结束时间,这样可以直接用函数返回两个时间之差,在判断是否>=80h即可,当月总数也能直接计算。

数据分类计算:

      状态1的数据:因为都在当月,可以直接计算;

      状态2的数据:我们需要注意的是分成两部分,第一个是开始日期+开始日期当月月末,第二个是结束时间+结束日期月初;

      状态3的数据:需要分成三个部分,第一个是开始日期+开始日期当月月末,第二个是中间月月初+中间月月末,第三个是结束时间+结束日期月初。

整体解析完成,知道思路后,实现过程:

        以年月为维度进行统计,那么我们需要一个时间维度表作为主表(统计展示的话,避免当月数据为空时月份不展示,所以以时间维度表作为主表去连接数据表)。使用WITH语法,可以写多个临时表然后集合使用,第一个临时表写时间维度表(维度表中暂时只有21年以及22年,所以只有去重后只有24条数据)。我们取维度表中的年,年月,以及每个月的月初以及月末(月初月末后边有用)。

 

时间维度表设计好后,第二个临时表取数据表的数据。

 

        接着利用表连接的机制去产生笛卡尔积,然后借助主表的时间给每条数据分配合适的开始日期与结束日期。以主表的年月为ON筛选器的条件,取   >=数据表的开始日期   AND  <= 数据表的结束日期。以其中一条数据看,可以看到在开始日期与结束日期中按月生成了笛卡尔积。这样我们就能够得到所有数据的按月扩散的明细表。这时候就可以开始筛选每一条数据的开始日期与结束日期了。

 

 每一条数据的开始日期与结束日期的选择:使用CASE WHEN 语法,

开始日期:

1、当主表年月=数据表开始日期年月时,输出数据表开始日期作为开始日期;

2、当主表年月>数据表开始日期年月  AND 主表年月<=数据表结束日期年月  取主表FIRSTDAY作为开始日期

结束日期:

1、主表年月<结束日期年月,输出主表LASTDAY;

2、主表年月=结束日期年月,输出数据表结束日期。

 

       从结果上可以看到一条数据原本从2021-10月开始到2022-04结束,已经通过笛卡尔积扩散到每个月,并且选定了合适且合理的开始日期以及结束日期,这样就可以以这个扩散表为明细表统计每个月的总数,以及延迟单的总数了。这样指标就可以计算出来了。

      总结:简单复现一下,主要说明如何通过笛卡尔积在合适的区间内扩散,生成我们需要的明细数据,实际上会更复杂一点,因为还有计划月的统计,具体到哪个小时划分,以及外来的时间参数(外来时间参数第一个首先影响到我们统计的基表数据--需要开始日期都<外来日期参数;第二个时在选择结束日期时需要加入外来日期参数的考虑)。

      整体实现SQL(时间维度表看上一章:利用存储过程生成时间维度表):

#主表日期,选择带上当月的月初以及月末。外来参数可控,具体计划月的划分,具体小时的划分,也可以在这里完成。这里的思想:得出我们需要的时间维度,然后通过年月与数据表年月进行区间内的笛卡尔积扩散,得到准确的数据扩散表,然后再选择判断扩散数据的分类,分别选择合适的开始日期以及结束日期。
WITH MONTHTABLE AS (
SELECT  DISTINCT
	T.`YEAR`,
	T.YEARMONTH,
	CAST(DATE_ADD(T.DATE, interval - day(T.DATE) + 1 day) AS DATETIME)FIRSTDAY,
	CAST(LAST_DAY(T.DATE) AS DATETIME)LASTDAY
FROM
	test_dim.dim_Time T
	WHERE 1=1 
	),  
	
	#数据表查询,原表样不需要处理,直接查询,这个位置看具体的表结构,以及数据格式
	DATATABLE AS (
	SELECT
		T.ORDERNUM,
		T.BEGINDATE,
		LEFT(T.BEGINDATE,7)BEGINDATEAA,
		T.ENDDATE,
	  LEFT(	T.ENDDATE,7)ENDDATEBB
	FROM
		test_dim.month_periodic_statistics T
	)
	



#左连接查询,利用笛卡尔积在年月区间内扩散,得到数据源表的扩散,然后再结合主表时间维度选择合适的开始与结束日期。
	SELECT 
	TT.YEARMONTH,
	TT.ORDERNUM,
	(CASE WHEN TT.YEARMONTH=TT.BEGINDATEAA THEN TT.BEGINDATE
	      WHEN TT.YEARMONTH>TT.BEGINDATEAA AND TT.YEARMONTH<=TT.ENDDATEBB THEN TT.FIRSTDAY
				ELSE NULL END
	)BEGINDATE,   -- 判断取开始日期
		(CASE WHEN TT.YEARMONTH<TT.ENDDATEBB THEN TT.LASTDAY
	        WHEN TT.YEARMONTH=TT.ENDDATEBB THEN TT.ENDDATE	
				ELSE NULL END	
	)ENDDATE  -- 判断取结束日期
	
	FROM
	(
	
	SELECT 
	A.YEARMONTH,A.FIRSTDAY,A.LASTDAY,B.ORDERNUM,B.BEGINDATE,B.BEGINDATEAA,B.ENDDATE,B.ENDDATEBB
	FROM MONTHTABLE A
	LEFT JOIN  DATATABLE B ON  A.YEARMONTH>=B.BEGINDATEAA   AND A.YEARMONTH<=ENDDATEBB
	WHERE 1=1 
	AND B.ORDERNUM='BLNO004'
	)TT
-- 	ORDER BY 

 测试数据表:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for month_periodic_statistics
-- ----------------------------
DROP TABLE IF EXISTS `month_periodic_statistics`;
CREATE TABLE `month_periodic_statistics`  (
  `ID` int(0) NOT NULL AUTO_INCREMENT,
  `ORDERNUM` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `BEGINDATE` datetime(0) NULL DEFAULT NULL,
  `ENDDATE` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of month_periodic_statistics
-- ----------------------------
INSERT INTO `month_periodic_statistics` VALUES (1, 'BLNO001', '2021-12-01 00:00:00', '2021-12-03 22:59:05');
INSERT INTO `month_periodic_statistics` VALUES (2, 'BLNO002', '2022-02-03 10:59:38', '2022-03-01 14:59:52');
INSERT INTO `month_periodic_statistics` VALUES (3, 'BLNO003', '2022-04-06 23:00:24', '2022-07-19 15:00:32');
INSERT INTO `month_periodic_statistics` VALUES (4, 'BLNO004', '2021-10-06 23:00:55', '2022-04-20 17:01:06');
INSERT INTO `month_periodic_statistics` VALUES (5, 'BLNO005', '2022-05-10 23:01:28', '2022-05-11 19:01:35');

SET FOREIGN_KEY_CHECKS = 1;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值