0


Python+大数据-知行教育(七)-学生出勤主题看板

Python+大数据-知行教育(七)-学生出勤主题看板

2. 学生出勤主题看板

2.1 需求分析

回顾:

涉及维度, 指标, 涉及表, 字段, 以及需要清洗的内容, 需要转换的内容, 如果有多个表, 表与表关联条件
  • 需求一: 统计指定时间段内,不同班级的出勤人数。打卡时间在上课前40分钟(否则认为无效)~上课时间点之内,且未早退,则为正常上课打卡。可以下钻到具体学生的出勤数据。跨天数据直接累加。
涉及维度:
    时间维度: 年 月 天 上午 下午 晚自习
    班级维度:
    学生维度:
涉及指标:
    出勤人数(正常出勤 + 迟到出勤)

计算公式: 打卡时间在上课前40分钟(否则认为无效)~上课时间点之内
  • 需求二: 统计指定时间段内,不同班级的学生出勤率。可以下钻到具体学生的出勤数据。出勤率=出勤人数/当日在读学员人数。
涉及维度:
    时间维度: 年 月 天 上午 下午 晚自习
    班级维度:
    学生维度:

涉及指标:
    出勤率: 出勤人数/当日在读学员人数
  • 需求三: 统计指定时间段内,不同班级的迟到人数。上课10分钟后视为迟到。可以下钻到具体学生的迟到数据。跨天数据直接累加。
涉及维度:
    时间维度: 年 月 天 上午 下午 晚自习
    班级维度:
    学生维度:

涉及指标:
    迟到人数: 

计算标准: 上课10分钟后视为迟到

正常出勤时间范围:  上课时间前40分钟内 ~ 上课时间后10分钟内
迟到出勤时间范围:  上课时间10分钟后 ~ 上课结束时间内
  • 需求四: 统计指定时间段内,不同班级的学生迟到率。上课10分钟后视为迟到。可以下钻到具体学生的迟到数据。迟到率=迟到人数/当日在读学员人数。
涉及维度:
    时间维度: 年 月 天 上午 下午 晚自习
    班级维度:
    学生维度:

涉及指标: 
    迟到率: 迟到人数/当日在读学员人数

涉及表: 
    course_table_upload_detail:  日志课程明细表 (课表)  (维度表)
    tbh_student_signin_record:   学生打卡记录表   (事实表)
    tbh_class_time_table: 班级作息时间表 (维度表)

关联条件: 
    学生打卡表.class_id = 课程表.class_id
    班级作息时间表.id = 学生打卡表.time_table_id

涉及到字段: 
    时间维度: 课程表.class_date
    班级维度: 课程表.class_id
    学生维度: 学生打卡表.student_id
    指标字段: 学生打卡表.signin_time(打卡时间)
        计算方案:  
            先判断是否出勤 
                情况1: 出勤了, 再次判断是否是正常出勤和迟到出勤
                情况2: 未出勤, 认为没有来
    指标判断指标:
        作息时间表: 
            morning_begin_time
            morning_end_time
            afternoon_begin_time
            afternoon_end_time
            evening_begin_time
            evening_end_time

过滤操作
    1) ifnull(ctud.content,'') != '' 相当于 ctud.content != null and ctud.content != ''
       转换为hive的操作
          nvl(ctud.content,'') != ''
    2) 将content中为开班典礼数据过滤掉
        ctud.content != '开班典礼'
    3) 确保打卡表中学生数据都是开启公屏
        学生打卡表.share_state=1
        
判断学生出勤状态: 0(正常出勤)   1(迟到出勤)     2(缺勤
    首先根据打卡时间, 如果打卡的时间在 上课的前40分钟内 ~ 上课截止时间内
        情况1: 出勤了, 接着判断 是否是正常出勤还是迟到出勤, 
            如果打卡时间在 上课的前40分钟内容 ~ 上课的开始时间后10分内, 认为正常出勤了  返回 0
            否则认为迟到出勤  返回 1
        情况2: 没有出去, 认为没有来   返回  2

一句话: 
    当查询的SQL有group by的操作的时候, select后只能出现group by出现过字段, 如果非要放置未出现的字段, 必须包裹在聚合函数内
  • 需求五: 统计指定时间段内,不同班级的请假人数。跨天数据直接累加。
涉及维度:
    时间维度: 年 月 天 上午 下午 晚自习
    班级维度:
涉及指标:
    请假人数
    
涉及表:
    student_leave_apply:  学生请假表 (事实表)
    tbh_class_time_table: 班级作息时间表 (维度表)
    course_table_upload_detail: 课程表 (维度表)

表关联条件: 
    学生请假表.class_id = 班级作息时间表.class_id
    学生请假表.class_id = 课程表.class_id

涉及字段: 
    时间维度:  课程表.class_date
    班级维度:  课程表.class_id
    指标字段:  请假表.student_id
        需要进行去重统计操作

过滤条件:
    课程表: 
        content 不能为空 为null 以及不能为 开班典礼
    
    获取有效的作息时间:
        课程表.class_date between 作息表.use_begin_date and 作息表.use_end_date
    
    学生请假表:保证请假数据有效的
        audit_state =1  -- 审核通过
        cancel_state = 0 -- 没有取消
        valid_state = 1  -- 有效的
    判断是否请假条件:
        请假的开始时间(请假表.begin_time) <= 上课的开始时间 (morning_begin_time |afternon_begin_time | evening_begin_time)
        请假的结束时间(请假表.end_time) >= 上课的开始时间(morning_begin_time |afternon_begin_time | evening_begin_time)
        
  • 需求六: 统计指定时间段内,不同班级的学生请假率。请假率=请假人数/当日在读学员人数。
涉及维度:
    时间维度: 年 月  天 上午 下午 晚自习
    班级维度:
涉及指标:
    请假率
  • 需求七: 统计指定时间段内,不同班级的旷课人数。跨天数据直接累加。旷课人数=当日在读学员人数-出勤人数-请假人数。
涉及维度: 
    时间维度: 年 月  天 上午 下午 晚自习
    班级维度:

涉及指标:  
    旷课人数

计算方案: 
    旷课人数=当日在读学员人数-出勤人数-请假人数。
  • 需求八: 统计指定时间段内,不同班级的学生旷课率。旷课率=旷课人数/当日在读学员人数。
涉及维度:
    时间维度: 年 月  天 上午 下午 晚自习
    班级维度:

涉及指标: 
    旷课率

旷课率=旷课人数/当日在读学员人数。    

总结需求:

第一类: 指标: 计算 出勤人数, 出勤率, 迟到人数, 迟到率

涉及维度:
    时间维度: 年  月  天  上午 下午 晚自习
    班级维度:
    学生维度:

涉及表: 
    course_table_upload_detail:  日志课程明细表 (课表)  (维度表)
    tbh_student_signin_record:   学生打卡记录表   (事实表)
    tbh_class_time_table: 班级作息时间表 (维度表)

关联条件: 
    学生打卡表.class_id = 课程表.class_id
    班级作息时间表.id = 学生打卡表.time_table_id

涉及到字段: 
    时间维度: 课程表.class_date
    班级维度: 课程表.class_id
    学生维度: 学生打卡表.student_id
    指标字段: 学生打卡表.signin_time(打卡时间)
        计算方案:  
            先判断是否出勤 
                情况1: 出勤了, 再次判断是否是正常出勤和迟到出勤
                情况2: 未出勤, 认为没有来
    指标判断指标:
        作息时间表: 
            morning_begin_time
            morning_end_time
            afternoon_begin_time
            afternoon_end_time
            evening_begin_time
            evening_end_time

过滤操作
    1) ifnull(ctud.content,'') != '' 相当于 ctud.content != null and ctud.content != ''
       转换为hive的操作
          nvl(ctud.content,'') != ''
    2) 将content中为开班典礼数据过滤掉
        ctud.content != '开班典礼'
    3) 确保打卡表中学生数据都是开启公屏
        学生打卡表.share_state=1
        
判断学生出勤状态: 0(正常出勤)   1(迟到出勤)     2(缺勤
    首先根据打卡时间, 如果打卡的时间在 上课的前40分钟内 ~ 上课截止时间内
        情况1: 出勤了, 接着判断 是否是正常出勤还是迟到出勤, 
            如果打卡时间在 上课的前40分钟内容 ~ 上课的开始时间后10分内, 认为正常出勤了  返回 0
            否则认为迟到出勤  返回 1
        情况2: 没有出去, 认为没有来   返回  2
    

第二类: 指标: 计算 请假人数, 请假率

涉及维度: 
    时间维度: 年  月  天  上午 下午 晚自习
    班级维度:

涉及表:
    student_leave_apply:  学生请假表 (事实表)
    tbh_class_time_table: 班级作息时间表 (维度表)
    course_table_upload_detail: 课程表 (维度表)

表关联条件: 
    学生请假表.class_id = 班级作息时间表.class_id
    学生请假表.class_id = 课程表.class_id

涉及字段: 
    时间维度:  课程表.class_date
    班级维度:  课程表.class_id
    指标字段:  请假表.student_id
        需要进行去重统计操作

过滤条件:
    课程表: 
        content 不能为空 为null 以及不能为 开班典礼
    
    获取有效的作息时间:
        课程表.class_date between 作息表.use_begin_date and 作息表.use_end_date
    
    学生请假表:保证请假数据有效的
        audit_state =1  -- 审核通过
        cancel_state = 0 -- 没有取消
        valid_state = 1  -- 有效的
    判断是否请假条件:
        请假的开始时间(请假表.begin_time) <= 上课的开始时间 (morning_begin_time |afternon_begin_time | evening_begin_time)
        请假的结束时间(请假表.end_time) >= 上课的开始时间(morning_begin_time |afternon_begin_time | evening_begin_time)

第三类: 计算旷课人数, 旷课率

涉及维度: 
    时间维度: 年  月  天  上午 下午 晚自习
    班级维度:
计算标准:
    旷课人数 = 当日在读人数 - 出勤人数(正常出勤+迟到出勤) -请假人数

2.2 业务数据准备工作

  • 第一步: 在mysql中, 创建一个业务库
createdatabase teach defaultcharacterset utf8mb4 collate utf8mb4_unicode_ci;
  • 第二步: 将课件中测试数据集导入到mysql的teach库中

image-20211007161531864

表1: 当日在读人数表
表2: 课程日期表
表3: 学生请假表
表4: 班级作息时间表
表5: 学生打卡记录表

2.3 建模分析

  • ODS层: 源数据层
作用: 对接数据源, 一般和数据源的表保持相同粒度
一般存放事实表以及少量的维度表

建表方案:  
    构建两张表 :  学生打卡记录表  和 学生请假表

在构建的时候, 需要额外添加一个分区字段: start_time(抽取数据的时间)
  • DIM层: 维度层
作用: 用于存储维度表的数据, 一般和数据源对表保持相同粒度

建表方案: 
    构建三个表:  课程日期表, 班级作息时间表, 当日在读人数表
    
在构建的时候, 需要额外添加一个分区字段: start_time(抽取数据的时间)    
  • DWD层: 明细层
作用:  1) 清洗转换操作   2) 少量的维度退化操作  (不需要)

清洗转换操作: 
    可做
        1) 日期转换为 年  月  日
        2) 过滤无效的请假数据
        3) 过滤没有开启公屏的数据

目前假设没有这些操作, 所以此时DWD层 是不需要存在的
  • DWM层: 中间层
作用: 维度退化  以及 提前聚合操作

处理逻辑: 先 分 在 合的操作

建表方案: 
    第一个表:  学生出勤状态表 
        作用: 用于统计每天每个班级, 每个学生的出勤状态(0(正常出勤) 1(迟到出勤) 2(没来))
        表字段 : 
           yearinfo, monthinfo,dayinfo, 班级id, 学生id, 上午出勤, 下午出勤, 晚自习出勤
    第二个表: 班级出勤人数表
        作用: 用于统计每天每个班级的出勤人数(出勤人数, 迟到出勤人数)
        表字段:  
            yearinfo, monthinfo,dayinfo, 班级id,上午出勤人数, 上午迟到人数, 下午出勤人数, 下午迟到人数, 晚上出勤人数, 晚上迟到人数
    第三个表: 班级请假人数表
        作用: 用于统计每天每个班级的请假人数
        表字段: 
            yearinfo, monthinfo,dayinfo, 班级id, 上午请假人数, 下午请假人数, 晚上请假人数
    第四个表: 班级旷课人数表
        作用: 用于统计每天每个班级的旷课人数
        表字段:
            yearinfo, monthinfo,dayinfo, 班级id, 上午旷课人数, 下午旷课人数, 晚上旷课人数
            
    第五个表:  班级指标汇总表  (提前聚合表)
        作用: 用于将前几个表相关的指标数据汇总起来, 同时计算出 相关比率操作 (统计每天的上午 下午 晚自习)
        表字段: 
            yearinfo, monthinfo,dayinfo, 班级id, 班级当日在读人数, 
                上午出勤人数, 上午出勤率, 下午出勤人数 下午出勤率, 晚上出勤人数, 晚上出勤率
                上午迟到人数, 上午迟到率, 下午迟到人数, 下午迟到率, 晚上迟到人数, 晚上迟到率
                上午请假人数, 上午请假率, 下午请假人数, 下午请假率, 晚上请假人数, 晚上请假率
                上午旷课人数, 上午旷课率, 下午旷课人数, 下午旷课率, 晚上旷课人数, 晚上旷课率
  • DWS层: 业务层
作用: 细化维度统计操作

建表方案: 
    yearinfo, monthinfo,dayinfo, 班级id, 班级当日在读人数, time_str,time_type
        上午出勤人数, 上午出勤率, 下午出勤人数 下午出勤率, 晚上出勤人数, 晚上出勤率
        上午迟到人数, 上午迟到率, 下午迟到人数, 下午迟到率, 晚上迟到人数, 晚上迟到率
        上午请假人数, 上午请假率, 下午请假人数, 下午请假率, 晚上请假人数, 晚上请假率
        上午旷课人数, 上午旷课率, 下午旷课人数, 下午旷课率, 晚上旷课人数, 晚上旷课率

2.4 建模操作

  • ODS层:
-- 学生打卡信息表:CREATETABLEIFNOTEXISTS itcast_ods.student_signin_ods (
    id                int,
    normal_class_flag intcomment'是否正课 1 正课 2 自习 3 休息',
    time_table_id     intcomment'作息时间id normal_class_flag=2 关联tbh_school_time_table 或者 normal_class_flag=1 关联 tbh_class_time_table',
    class_id          intcomment'班级id',
    student_id        intcomment'学员id',
    signin_time       String       comment'签到时间',
    signin_date       String          comment'签到日期',
    inner_flag        intcomment'内外网标志  0 外网 1 内网',
    signin_type       intcomment'签到类型 1 心跳打卡 2 老师补卡 3 直播打卡',
    share_state       intcomment'共享屏幕状态 0 否 1是  在上午或下午段有共屏记录,则该段所有记录该字段为1,内网默认为1 外网默认为0   (暂不用)',
    inner_ip          String          comment'内网ip地址',
    create_time       String       comment'创建时间')comment'学生打卡记录表'
PARTITIONED BY(dt STRING)ROW FORMAT DELIMITED 
FIELDSTERMINATEDBY'\t'
stored as orcfile
TBLPROPERTIES ('orc.compress'='SNAPPY','orc.bloom.filter.columns'='time_table_id,class_id,signin_date,share_state');-- 学生请假信息表:CREATETABLEIFNOTEXISTS itcast_ods.student_leave_apply_ods (
    id              int,
    class_id        intcomment'班级id',
    student_id      intcomment'学员id',
    audit_state     intcomment'审核状态 0 待审核 1 通过 2 不通过',
    audit_person    intcomment'审核人',
    audit_time      String      comment'审核时间',
    audit_remark    String     comment'审核备注',
    leave_type      intcomment'请假类型  1 请假 2 销假 (查询是否请假不用过滤此类型,通过有效状态来判断)',
    leave_reason    intcomment'请假原因  1 事假 2 病假',
    begin_time      String      comment'请假开始时间',
    begin_time_type intcomment'1:上午 2:下午 3:晚自习',
    end_time        String      comment'请假结束时间',
    end_time_type   intcomment'1:上午 2:下午 3:晚自习',
    days            floatcomment'请假/已休天数',
    cancel_state    intcomment'撤销状态  0 未撤销 1 已撤销',
    cancel_time     String      comment'撤销时间',
    old_leave_id    intcomment'原请假id,只有leave_type =2 销假的时候才有',
    leave_remark    String     comment'请假/销假说明',
    valid_state     intcomment'是否有效(0:无效 1:有效)',
    create_time     String      comment'创建时间')comment'学生请假申请表'
PARTITIONED BY(dt STRING)ROW FORMAT DELIMITED 
FIELDSTERMINATEDBY'\t'
stored as orcfile
TBLPROPERTIES ('orc.compress'='SNAPPY','orc.bloom.filter.columns'='class_id,audit_state,cancel_state,valid_state');
  • DIM层:
-- 日期课程表CREATETABLEIFNOTEXISTS itcast_dimen.course_table_upload_detail_dimen
(
    id                  intcomment'id',
    base_id             intcomment'课程主表id',
    class_id            intcomment'班级id',
    class_date          STRING      comment'上课日期',
    content             STRING     comment'课程内容',
    teacher_id          intcomment'老师id',
    teacher_name        STRING     comment'老师名字',
    job_number          STRING     comment'工号',
    classroom_id        intcomment'教室id',
    classroom_name      STRING     comment'教室名称',
    is_outline          intcomment'是否大纲 0 否 1 是',
    class_mode          intcomment'上课模式 0 传统全天 1 AB上午 2 AB下午 3 线上直播',
    is_stage_exam       intcomment'是否阶段考试(0:否 1:是)',
    is_pay              intcomment'代课费(0:无 1:有)',
    tutor_teacher_id    intcomment'晚自习辅导老师id',
    tutor_teacher_name  STRING     comment'辅导老师姓名',
    tutor_job_number    STRING     comment'晚自习辅导老师工号',
    is_subsidy          intcomment'晚自习补贴(0:无 1:有)',
    answer_teacher_id   intcomment'答疑老师id',
    answer_teacher_name STRING     comment'答疑老师姓名',
    answer_job_number   STRING     comment'答疑老师工号',
    remark              STRING        comment'备注',
    create_time         STRING      comment'创建时间')comment'班级课表明细表'
PARTITIONED BY(dt STRING)ROW FORMAT DELIMITED 
FIELDSTERMINATEDBY'\t'
stored as orcfile
TBLPROPERTIES ('orc.compress'='SNAPPY','orc.bloom.filter.columns'='class_id,class_date');-- 班级作息时间表CREATETABLEIFNOTEXISTS itcast_dimen.class_time_dimen (
    id                     int,
    class_id              intcomment'班级id',
    morning_template_id   intcomment'上午出勤模板id',
    morning_begin_time    STRING         comment'上午开始时间',
    morning_end_time      STRING         comment'上午结束时间',
    afternoon_template_id intcomment'下午出勤模板id',
    afternoon_begin_time  STRING         comment'下午开始时间',
    afternoon_end_time    STRING         comment'下午结束时间',
    evening_template_id   intcomment'晚上出勤模板id',
    evening_begin_time    STRING         comment'晚上开始时间',
    evening_end_time      STRING         comment'晚上结束时间',
    use_begin_date        STRING         comment'使用开始日期',
    use_end_date          STRING         comment'使用结束日期',
    create_time           STRING       comment'创建时间',
    create_person         intcomment'创建人',
    remark                STRING      comment'备注')comment'班级作息时间表'
PARTITIONED BY(dt STRING)ROW FORMAT DELIMITED 
FIELDSTERMINATEDBY'\t'
stored as orcfile
TBLPROPERTIES ('orc.compress'='SNAPPY','orc.bloom.filter.columns'='id,class_id');-- 当日在读人数表:CREATETABLEIFNOTEXISTS itcast_dimen.class_studying_student_count_dimen (
    id                     int,
    school_id              intcomment'校区id',
    subject_id             intcomment'学科id',
    class_id               intcomment'班级id',
    studying_student_count intcomment'在读班级人数',
    studying_date          STRING comment'在读日期')comment'在读班级的每天在读学员人数'
PARTITIONED BY(dt STRING)ROW FORMAT DELIMITED 
FIELDSTERMINATEDBY'\t'
stored as orcfile
TBLPROPERTIES ('orc.compress'='SNAPPY','orc.bloom.filter.columns'='studying_student_count,studying_date');
  • DWM层:
-- 学生出勤状态信息表 CREATETABLEIFNOTEXISTS itcast_dwm.student_attendance_dwm (
    dateinfo        String        comment'日期',
    class_id          intcomment'班级id',
    student_id        intcomment'学员id',
    morning_att       String       comment'上午出勤情况:0.正常出勤、1.迟到、2.其他(请假+旷课)',
    afternoon_att    String        comment'下午出勤情况:0.正常出勤、1.迟到、2.其他(请假+旷课)',
    evening_att       String        comment'晚自习出勤情况:0.正常出勤、1.迟到、2.其他(请假+旷课)')comment'学生出勤(正常出勤和迟到)数据'
PARTITIONED BY(yearinfo STRING, monthinfo STRING, dayinfo STRING)ROW FORMAT DELIMITED 
FIELDSTERMINATEDBY'\t'
stored as orcfile
TBLPROPERTIES ('orc.compress'='SNAPPY');-- 班级出勤人数表: CREATETABLEIFNOTEXISTS itcast_dwm.class_attendance_dwm (
    dateinfo        String        comment'日期',
    class_id          intcomment'班级id',
    morning_att_count       String         comment'上午出勤人数',
    afternoon_att_count      String        comment'下午出勤人数',
    evening_att_count       String        comment'晚自习出勤人数',
    morning_late_count       String         comment'上午迟到人数',
    afternoon_late_count      String        comment'下午迟到人数',
    evening_late_count       String        comment'晚自习迟到人数')comment'学生出勤(正常出勤和迟到)数据'
PARTITIONED BY(yearinfo STRING, monthinfo STRING, dayinfo STRING)ROW FORMAT DELIMITED
FIELDSTERMINATEDBY'\t'
stored as orcfile
TBLPROPERTIES ('orc.compress'='SNAPPY');-- 班级请假人数表CREATETABLEIFNOTEXISTS itcast_dwm.class_leave_dwm (
    dateinfo      String        comment'日期',
    class_id        intcomment'班级id',
    morning_leave_count       String       comment'上午请假人数',
    afternoon_leave_count    String        comment'下午请假人数',
    evening_leave_count       String        comment'晚自习请假人数')comment'班级请假数据统计'
PARTITIONED BY(yearinfo STRING, monthinfo STRING, dayinfo STRING)ROW FORMAT DELIMITED 
FIELDSTERMINATEDBY'\t'
stored as orcfile
TBLPROPERTIES ('orc.compress'='SNAPPY');-- 班级旷课人数表CREATETABLEIFNOTEXISTS itcast_dwm.class_truant_dwm (
    dateinfo      String        comment'日期',
    class_id        intcomment'班级id',
    morning_truant_count       String          comment'上午旷课人数',
    afternoon_truant_count   String        comment'下午旷课人数',
    evening_truant_count       String        comment'晚自习旷课人数')comment'班级请假数据统计'
PARTITIONED BY(yearinfo STRING, monthinfo STRING, dayinfo STRING)ROW FORMAT DELIMITED 
FIELDSTERMINATEDBY'\t'
stored as orcfile
TBLPROPERTIES ('orc.compress'='SNAPPY');-- 汇总表CREATETABLEIFNOTEXISTS itcast_dwm.class_all_dwm (
    dateinfo      String        comment'日期',
    class_id        intcomment'班级id',
   studying_student_count intcomment'在读班级人数',
    morning_att_count       String         comment'上午出勤人数',
   morning_att_ratio       String         comment'上午出勤率',
    afternoon_att_count      String        comment'下午出勤人数',
   afternoon_att_ratio      String        comment'下午出勤率',
    evening_att_count       String        comment'晚自习出勤人数',
   evening_att_ratio       String        comment'晚自习出勤率',
   morning_late_count       String        comment'上午迟到人数',
   morning_late_ratio       String        comment'上午迟到率',
    afternoon_late_count     String        comment'下午迟到人数',
   afternoon_late_ratio     String        comment'下午迟到率',
    evening_late_count       String        comment'晚自习迟到人数',
   evening_late_ratio       String        comment'晚自习迟到率',
   morning_leave_count       String       comment'上午请假人数',
   morning_leave_ratio       String       comment'上午请假率',
    afternoon_leave_count    String        comment'下午请假人数',
   afternoon_leave_ratio    String        comment'下午请假率',
    evening_leave_count       String        comment'晚自习请假人数',
   evening_leave_ratio       String        comment'晚自习请假率',
    morning_truant_count       String          comment'上午旷课人数',
   morning_truant_ratio       String          comment'上午旷课率',
    afternoon_truant_count   String        comment'下午旷课人数',
   afternoon_truant_ratio   String        comment'下午旷课率',
    evening_truant_count       String        comment'晚自习旷课人数',
   evening_truant_ratio       String        comment'晚自习旷课率')comment'班级请假数据统计'
PARTITIONED BY(yearinfo STRING, monthinfo STRING, dayinfo STRING)ROW FORMAT DELIMITED 
FIELDSTERMINATEDBY'\t'
stored as orcfile
TBLPROPERTIES ('orc.compress'='SNAPPY');
  • DWS层:
CREATETABLEIFNOTEXISTS itcast_dws.class_attendance_dws (
    dateinfo      String        comment'日期',
    class_id        intcomment'班级id',
   studying_student_count intcomment'在读班级人数',
    morning_att_count       String         comment'上午出勤人数',
   morning_att_ratio       String         comment'上午出勤率',
    afternoon_att_count      String        comment'下午出勤人数',
   afternoon_att_ratio      String        comment'下午出勤率',
    evening_att_count       String        comment'晚自习出勤人数',
   evening_att_ratio       String        comment'晚自习出勤率',
   morning_late_count       String        comment'上午迟到人数',
   morning_late_ratio       String        comment'上午迟到率',
    afternoon_late_count     String        comment'下午迟到人数',
   afternoon_late_ratio     String        comment'下午迟到率',
    evening_late_count       String        comment'晚自习迟到人数',
   evening_late_ratio       String        comment'晚自习迟到率',
   morning_leave_count       String       comment'上午请假人数',
   morning_leave_ratio       String       comment'上午请假率',
    afternoon_leave_count    String        comment'下午请假人数',
   afternoon_leave_ratio    String        comment'下午请假率',
    evening_leave_count       String        comment'晚自习请假人数',
   evening_leave_ratio       String        comment'晚自习请假率',
    morning_truant_count       String          comment'上午旷课人数',
   morning_truant_ratio       String          comment'上午旷课率',
    afternoon_truant_count   String        comment'下午旷课人数',
   afternoon_truant_ratio   String        comment'下午旷课率',
    evening_truant_count       String        comment'晚自习旷课人数',
   evening_truant_ratio       String        comment'晚自习旷课率',
   time_type            STRING       COMMENT'聚合时间类型:1、按小时聚合;2、按天聚合;3、按周聚合;4、按月聚合;5、按年聚合。')comment'班级请假数据统计'
PARTITIONED BY(yearinfo STRING, monthinfo STRING, dayinfo STRING)ROW FORMAT DELIMITED 
FIELDSTERMINATEDBY'\t'
stored as orcfile
TBLPROPERTIES ('orc.compress'='SNAPPY');

2.5 数据采集操作

​ 数据: 目前存储在MySQL中

​ 目的地: 放置hive中

​ 技术: 采用 apache sqoop 来解决

  • ODS层数据采集操作
# 学生打卡记录表

sqoop import\
--connect jdbc:mysql://192.168.52.150:3306/teach \
--username root \
--password 123456\
--query 'select *,"2021-10-07" as dt  from tbh_student_signin_record where 1=1 and $CONDITIONS'\
--hcatalog-database itcast_ods \
--hcatalog-table student_signin_ods \
-m 1# 学生请假信息表

sqoop import\
--connect jdbc:mysql://192.168.52.150:3306/teach \
--username root \
--password 123456\
--query 'select *,"2021-10-07" as dt  from student_leave_apply where 1=1 and $CONDITIONS'\
--hcatalog-database itcast_ods \
--hcatalog-table student_leave_apply_ods \
-m 1
  • DIM层:
# 当日在读人数表
sqoop import\
--connect jdbc:mysql://192.168.52.150:3306/teach \
--username root \
--password 123456\
--query 'select *,"2021-10-07" as dt  from class_studying_student_count where 1=1 and $CONDITIONS'\
--hcatalog-database itcast_dimen \
--hcatalog-table class_studying_student_count_dimen \
-m 1# 日期课程表
sqoop import\
--connect jdbc:mysql://192.168.52.150:3306/teach \
--username root \
--password 123456\
--query 'select *,"2021-10-07" as dt  from course_table_upload_detail where 1=1 and $CONDITIONS'\
--hcatalog-database itcast_dimen \
--hcatalog-table course_table_upload_detail_dimen \
-m 1# 作息时间表
sqoop import\
--connect jdbc:mysql://192.168.52.150:3306/teach \
--username root \
--password 123456\
--query 'select *,"2021-10-07" as dt  from tbh_class_time_table where 1=1 and $CONDITIONS'\
--hcatalog-database itcast_dimen \
--hcatalog-table  class_time_dimen \
-m 1

2.6 数据清洗转换操作

​ 目的: 主要是用于从ODS以及DIM层 将数据灌入到DWM层操作

  • 生成学生出勤状态信息表
涉及表: 
    course_table_upload_detail:  日志课程明细表 (课表)  (维度表)
    tbh_student_signin_record:   学生打卡记录表   (事实表)
    tbh_class_time_table: 班级作息时间表 (维度表)

关联条件: 
    学生打卡表.class_id = 课程表.class_id
    班级作息时间表.id = 学生打卡表.time_table_id

涉及到字段: 
    时间维度: 课程表.class_date
    班级维度: 课程表.class_id
    学生维度: 学生打卡表.student_id
    指标字段: 学生打卡表.signin_time(打卡时间)
        计算方案:  
            先判断是否出勤 
                情况1: 出勤了, 再次判断是否是正常出勤和迟到出勤
                情况2: 未出勤, 认为没有来
    指标判断指标:
        作息时间表: 
            morning_begin_time
            morning_end_time
            afternoon_begin_time
            afternoon_end_time
            evening_begin_time
            evening_end_time

过滤操作
    1) ifnull(ctud.content,'') != '' 相当于 ctud.content != null and ctud.content != ''
       转换为hive的操作
          nvl(ctud.content,'') != ''
    2) 将content中为开班典礼数据过滤掉
        ctud.content != '开班典礼'
    3) 确保打卡表中学生数据都是开启公屏
        学生打卡表.share_state=1
    

基本SQL的实现: 相关的出勤状态未完成

select 
    ctudd.class_date,
    ctudd.class_id,
    student_id,''as morning_att,--未实现''as afternoon_att,--未实现''as  evening_att,--未实现
    substr(ctudd.class_date,1,4)as yearinfo,
    substr(ctudd.class_date,6,2)as monthinfo,
    substr(ctudd.class_date,9,2)as dayinfo
from(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd
     leftjoin(select*from itcast_ods.student_signin_ods where share_state =1) sso on sso.class_id = ctudd.class_id
     leftjoin itcast_dimen.class_time_dimen ctd on ctd.id = sso.time_table_id
groupby ctudd.class_date , ctudd.class_id,sso.student_id;

探讨如何判断学生出勤状态呢?

基于学生的打卡时间  以上午为例
    如果学生的打卡时间 在 上午上课开始时间前40分钟内  ~~ 上午上课截止时间内
        认为 学生出勤了
            此时接着判断, 如何学生的打卡时间在 上午上课开始时间前40分钟内容  ~~ 上午上课开始时间后10分钟内
                认为 学生是正常出勤  返回 0
                否则 认为学生是迟到出勤 返回 1
        
        否则认为学生没有出勤, 直接返回 2

接着 编写一个伪代码. 实现这个上述的逻辑:

select 
    ctudd.class_date,
    ctudd.class_id,
    student_id,if(sum(if( 
                sso.signin_time 
                    between ctd.morning_begin_time -40and  ctd.morning_end_time 
            ,1,0))>0,-- 如果大于0 认为当天的打卡记录中, 一定是有出勤的打卡记录, 如果小于等于0 认为没有出勤if(sum(if(
                 sso.signin_time 
                    between ctd.morning_begin_time -40and  ctd.morning_begin_time +10,1,0))>0,-- 如果大于0, 认为当天打卡记录中, 一定是有正常出勤的记录, 否则认为迟到出勤0,1),2)as morning_att,--伪代码实现''as afternoon_att,--未实现''as  evening_att,--未实现
    substr(ctudd.class_date,1,4)as yearinfo,
    substr(ctudd.class_date,6,2)as monthinfo,
    substr(ctudd.class_date,9,2)as dayinfo
from(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd
     leftjoin(select*from itcast_ods.student_signin_ods where share_state =1) sso on sso.class_id = ctudd.class_id
     leftjoin itcast_dimen.class_time_dimen ctd on ctd.id = sso.time_table_id
groupby ctudd.class_date , ctudd.class_id,sso.student_id;

探讨. 如何实现日期的 相加 和 相减 (对分钟处理)

在hive中, 并没有发现可以对分钟加减的函数, 只有对天的加减函数, 但是不符合要求, 如何解决呢? 
    可以尝试将日期数据转换为时间戳, 然后对时间戳进行加减处理 即可解决问题
    select unix_timestamp('2021-10-08 15:40:30','yyyy-MM-dd HH:mm:ss') - 40*60;

发现: 作息时间表的 上课时间内容, 只有 时 分 秒 没有 年 月 日

此时, 无法直接将其转换为时间戳的, 如何解决呢?  
    此时可以将上课的日期时间加入

实现上午的学生出勤统计SQL:

select 
    ctudd.class_date,
    ctudd.class_id,
    student_id,if(sum(if( 
                unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_end_time),'yyyy-MM-dd HH:mm:ss'),1,0))>0,-- 如果大于0 认为当天的打卡记录中, 一定是有出勤的打卡记录, 如果小于等于0 认为没有出勤if(sum(if(
                 unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_begin_time),'yyyy-MM-dd HH:mm:ss')+10*60,1,0))>0,-- 如果大于0, 认为当天打卡记录中, 一定是有正常出勤的记录, 否则认为迟到出勤0,1),2)as morning_att,''as afternoon_att,--未实现''as  evening_att,--未实现
    substr(ctudd.class_date,1,4)as yearinfo,
    substr(ctudd.class_date,6,2)as monthinfo,
    substr(ctudd.class_date,9,2)as dayinfo
from(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd
     leftjoin(select*from itcast_ods.student_signin_ods where share_state =1) sso on sso.class_id = ctudd.class_id
     leftjoin itcast_dimen.class_time_dimen ctd on ctd.id = sso.time_table_id
groupby ctudd.class_date , ctudd.class_id,sso.student_id;select unix_timestamp('2021-10-08 15:40:30','yyyy-MM-dd HH:mm:ss')-40*60;-- 1633676430
  • 最终实现:
select 
    ctudd.class_date,
    ctudd.class_id,
    student_id,if(sum(if( 
                unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_end_time),'yyyy-MM-dd HH:mm:ss'),1,0))>0,-- 如果大于0 认为当天的打卡记录中, 一定是有出勤的打卡记录, 如果小于等于0 认为没有出勤if(sum(if(
                 unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_begin_time),'yyyy-MM-dd HH:mm:ss')+10*60,1,0))>0,-- 如果大于0, 认为当天打卡记录中, 一定是有正常出勤的记录, 否则认为迟到出勤0,1),2)as morning_att,if(sum(if( 
                unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.afternoon_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.afternoon_end_time),'yyyy-MM-dd HH:mm:ss'),1,0))>0,-- 如果大于0 认为当天的打卡记录中, 一定是有出勤的打卡记录, 如果小于等于0 认为没有出勤if(sum(if(
                 unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.afternoon_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.afternoon_begin_time),'yyyy-MM-dd HH:mm:ss')+10*60,1,0))>0,-- 如果大于0, 认为当天打卡记录中, 一定是有正常出勤的记录, 否则认为迟到出勤0,1),2)as afternoon_att,if(sum(if( 
                unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.evening_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.evening_end_time),'yyyy-MM-dd HH:mm:ss'),1,0))>0,-- 如果大于0 认为当天的打卡记录中, 一定是有出勤的打卡记录, 如果小于等于0 认为没有出勤if(sum(if(
                 unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.evening_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.evening_begin_time),'yyyy-MM-dd HH:mm:ss')+10*60,1,0))>0,-- 如果大于0, 认为当天打卡记录中, 一定是有正常出勤的记录, 否则认为迟到出勤0,1),2)as  evening_att,--未实现
    substr(ctudd.class_date,1,4)as yearinfo,
    substr(ctudd.class_date,6,2)as monthinfo,
    substr(ctudd.class_date,9,2)as dayinfo
from(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd
     leftjoin(select*from itcast_ods.student_signin_ods where share_state =1) sso on sso.class_id = ctudd.class_id
     leftjoin itcast_dimen.class_time_dimen ctd on ctd.id = sso.time_table_id
groupby ctudd.class_date , ctudd.class_id,sso.student_id;

将SQL的结果灌入到DWM层

set hive.auto.convert.join=false;--分区SET hive.exec.dynamic.partition=true;SET hive.exec.dynamic.partition.mode=nonstrict;set hive.exec.max.dynamic.partitions.pernode=10000;set hive.exec.max.dynamic.partitions=100000;set hive.exec.max.created.files=150000;--hive压缩set hive.exec.compress.intermediate=true;set hive.exec.compress.output=true;--写入时压缩生效set hive.exec.orc.compression.strategy=COMPRESSION;--分桶--set hive.enforce.bucketing=true;--set hive.enforce.sorting=true;--set hive.optimize.bucketmapjoin = true;--set hive.auto.convert.sortmerge.join=true;--set hive.auto.convert.sortmerge.join.noconditionaltask=true;--并行执行set hive.exec.parallel=true;set hive.exec.parallel.thread.number=8;--小文件合并-- set mapred.max.split.size=2147483648;-- set mapred.min.split.size.per.node=1000000000;-- set mapred.min.split.size.per.rack=1000000000;--矢量化查询set hive.vectorized.execution.enabled=true;--关联优化器set hive.optimize.correlation=true;--读取零拷贝set hive.exec.orc.zerocopy=true;--join数据倾斜set hive.optimize.skewjoin=true;-- set hive.skewjoin.key=100000;set hive.optimize.skewjoin.compiletime=true;set hive.optimize.union.remove=true;-- group倾斜set hive.groupby.skewindata=true;insertintotable itcast_dwm.student_attendance_dwm partition(yearinfo,monthinfo,dayinfo)select 
    ctudd.class_date,
    ctudd.class_id,
    student_id,if(sum(if( 
                unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_end_time),'yyyy-MM-dd HH:mm:ss'),1,0))>0,-- 如果大于0 认为当天的打卡记录中, 一定是有出勤的打卡记录, 如果小于等于0 认为没有出勤if(sum(if(
                 unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.morning_begin_time),'yyyy-MM-dd HH:mm:ss')+10*60,1,0))>0,-- 如果大于0, 认为当天打卡记录中, 一定是有正常出勤的记录, 否则认为迟到出勤0,1),2)as morning_att,if(sum(if( 
                unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.afternoon_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.afternoon_end_time),'yyyy-MM-dd HH:mm:ss'),1,0))>0,-- 如果大于0 认为当天的打卡记录中, 一定是有出勤的打卡记录, 如果小于等于0 认为没有出勤if(sum(if(
                 unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.afternoon_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.afternoon_begin_time),'yyyy-MM-dd HH:mm:ss')+10*60,1,0))>0,-- 如果大于0, 认为当天打卡记录中, 一定是有正常出勤的记录, 否则认为迟到出勤0,1),2)as afternoon_att,if(sum(if( 
                unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.evening_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.evening_end_time),'yyyy-MM-dd HH:mm:ss'),1,0))>0,-- 如果大于0 认为当天的打卡记录中, 一定是有出勤的打卡记录, 如果小于等于0 认为没有出勤if(sum(if(
                 unix_timestamp(sso.signin_time,'yyyy-MM-dd HH:mm:ss')between unix_timestamp(concat(ctudd.class_date,' ',ctd.evening_begin_time),'yyyy-MM-dd HH:mm:ss')-40*60and  unix_timestamp(concat(ctudd.class_date,' ',ctd.evening_begin_time),'yyyy-MM-dd HH:mm:ss')+10*60,1,0))>0,-- 如果大于0, 认为当天打卡记录中, 一定是有正常出勤的记录, 否则认为迟到出勤0,1),2)as  evening_att,--未实现
    substr(ctudd.class_date,1,4)as yearinfo,
    substr(ctudd.class_date,6,2)as monthinfo,
    substr(ctudd.class_date,9,2)as dayinfo
from(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd
     leftjoin(select*from itcast_ods.student_signin_ods where share_state =1) sso on sso.class_id = ctudd.class_id
     leftjoin itcast_dimen.class_time_dimen ctd on ctd.id = sso.time_table_id
where ctudd.class_date in('2019-09-03','2019-09-04','2019-09-05')groupby ctudd.class_date , ctudd.class_id,sso.student_id;
  • 班级出勤人数表SQL的实现
select 
    dateinfo,
    class_id,count(casewhen morning_att in('0','1')then student_id
            elsenullend)as morning_att_count,count(casewhen afternoon_att in('0','1')then student_id
            elsenullend)as afternoon_att_count,count(casewhen evening_att in('0','1')then student_id
            elsenullend)as evening_att_count,sum(casewhen morning_att ='1'then1else0end)as morning_late_count,sum(casewhen afternoon_att ='1'then1else0end)as afternoon_late_count,sum(casewhen evening_att ='1'then1else0end)as evening_late_count,

    yearinfo,
    monthinfo,
    dayinfo
from itcast_dwm.student_attendance_dwm
groupby  dateinfo,yearinfo,monthinfo,dayinfo,class_id;

将查询的结果灌入到班级出勤信息表中

insertintotable itcast_dwm.class_attendance_dwm partition(yearinfo,monthinfo,dayinfo)select 
    dateinfo,
    class_id,count(casewhen morning_att in('0','1')then student_id
            elsenullend)as morning_att_count,count(casewhen afternoon_att in('0','1')then student_id
            elsenullend)as afternoon_att_count,count(casewhen evening_att in('0','1')then student_id
            elsenullend)as evening_att_count,sum(casewhen morning_att ='1'then1else0end)as morning_late_count,sum(casewhen afternoon_att ='1'then1else0end)as afternoon_late_count,sum(casewhen evening_att ='1'then1else0end)as evening_late_count,

    yearinfo,
    monthinfo,
    dayinfo
from itcast_dwm.student_attendance_dwm
groupby  dateinfo,yearinfo,monthinfo,dayinfo,class_id;
  • 班级请假人数表的统计生成
涉及表:
    student_leave_apply:  学生请假表 (事实表)
    tbh_class_time_table: 班级作息时间表 (维度表)
    course_table_upload_detail: 课程表 (维度表)

表关联条件: 
    学生请假表.class_id = 班级作息时间表.class_id
    学生请假表.class_id = 课程表.class_id

涉及字段: 
    时间维度:  课程表.class_date
    班级维度:  课程表.class_id
    指标字段:  请假表.student_id
        需要进行去重统计操作

过滤条件:
    课程表: 
        content 不能为空 为null 以及不能为 开班典礼
    
    获取有效的作息时间:
        课程表.class_date between 作息表.use_begin_date and 作息表.use_end_date
    
    学生请假表:保证请假数据有效的
        audit_state =1  -- 审核通过
        cancel_state = 0 -- 没有取消
        valid_state = 1  -- 有效的
    判断是否请假条件:
        请假的开始时间(请假表.begin_time) <= 上课的开始时间 (morning_begin_time |afternon_begin_time | evening_begin_time)
        请假的结束时间(请假表.end_time) >= 上课的开始时间(morning_begin_time |afternon_begin_time | evening_begin_time)

由于库表没有一条符合条件的请假数据, 所以在这里模拟一条请假数据

INSERTINTO itcast_ods.student_leave_apply_ods partition(dt)values(125,5032,119142,1,3491,'2019-09-08 16:42:29','狂犬疫苗最后一针',1,2,'2019-09-03 08:00:00',2,'2019-09-03 23:30:00',2,1,0,null,null,'医院打针,狂犬疫苗最后一针了',1,'2019-09-02 08:56:54','2020-07-07');

计算上午的每天各个班级请假人数

select
    ctudd.class_date,
    ctudd.class_id,count(distinct sla.student_id)as morning_leave_count
from(select*from itcast_ods.student_leave_apply_ods where audit_state =1and cancel_state =0and valid_state =1) sla
    join itcast_dimen.class_time_dimen ctd on sla.class_id = ctd.class_id
    join(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd on ctudd.class_id = sla.class_id
where ctudd.class_date between ctd.use_begin_date  and  ctd.use_end_date 
    and concat(ctudd.class_date,' ',ctd.morning_begin_time)>= sla.begin_time
    and concat(ctudd.class_date,' ',ctd.morning_begin_time)<= sla.end_time
groupby ctudd.class_date,ctudd.class_id;

计算下午的每天各个班级的请假人数

select
    ctudd.class_date,
    ctudd.class_id,count(distinct sla.student_id)as afternoon_leave_count
from(select*from itcast_ods.student_leave_apply_ods where audit_state =1and cancel_state =0and valid_state =1) sla
    join itcast_dimen.class_time_dimen ctd on sla.class_id = ctd.class_id
    join(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd on ctudd.class_id = sla.class_id
where ctudd.class_date between ctd.use_begin_date  and  ctd.use_end_date 
    and concat(ctudd.class_date,' ',ctd.afternoon_begin_time)>= sla.begin_time
    and concat(ctudd.class_date,' ',ctd.afternoon_begin_time)<= sla.end_time
groupby ctudd.class_date,ctudd.class_id;

计算晚自习的每天各个班级的请假人数

select
    ctudd.class_date,
    ctudd.class_id,count(distinct sla.student_id)as evening_leave_count
from(select*from itcast_ods.student_leave_apply_ods where audit_state =1and cancel_state =0and valid_state =1) sla
    join itcast_dimen.class_time_dimen ctd on sla.class_id = ctd.class_id
    join(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd on ctudd.class_id = sla.class_id
where ctudd.class_date between ctd.use_begin_date  and  ctd.use_end_date 
    and concat(ctudd.class_date,' ',ctd.evening_begin_time)>= sla.begin_time
    and concat(ctudd.class_date,' ',ctd.evening_begin_time)<= sla.end_time
groupby ctudd.class_date,ctudd.class_id;

目前需要将三个结果的数据合并在一起, 如何实现呢?

image-20221104172727279

思考如何实现? 全外连接(满外) full join    

高清图查看 图片目录

image-20221104172621381

image-20221104172657237

实现最终的查询的SQL:

with A as(select
    ctudd.class_date,
    ctudd.class_id,count(distinct sla.student_id)as morning_leave_count
from(select*from itcast_ods.student_leave_apply_ods where audit_state =1and cancel_state =0and valid_state =1) sla
    join itcast_dimen.class_time_dimen ctd on sla.class_id = ctd.class_id
    join(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd on ctudd.class_id = sla.class_id
where ctudd.class_date between ctd.use_begin_date  and  ctd.use_end_date 
    and concat(ctudd.class_date,' ',ctd.morning_begin_time)>= sla.begin_time
    and concat(ctudd.class_date,' ',ctd.morning_begin_time)<= sla.end_time
groupby ctudd.class_date,ctudd.class_id),
    B AS(select
    ctudd.class_date,
    ctudd.class_id,count(distinct sla.student_id)as afternoon_leave_count
from(select*from itcast_ods.student_leave_apply_ods where audit_state =1and cancel_state =0and valid_state =1) sla
    join itcast_dimen.class_time_dimen ctd on sla.class_id = ctd.class_id
    join(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd on ctudd.class_id = sla.class_id
where ctudd.class_date between ctd.use_begin_date  and  ctd.use_end_date 
    and concat(ctudd.class_date,' ',ctd.afternoon_begin_time)>= sla.begin_time
    and concat(ctudd.class_date,' ',ctd.afternoon_begin_time)<= sla.end_time
groupby ctudd.class_date,ctudd.class_id),
    C AS(select
    ctudd.class_date,
    ctudd.class_id,count(distinct sla.student_id)as evening_leave_count
from(select*from itcast_ods.student_leave_apply_ods where audit_state =1and cancel_state =0and valid_state =1) sla
    join itcast_dimen.class_time_dimen ctd on sla.class_id = ctd.class_id
    join(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd on ctudd.class_id = sla.class_id
where ctudd.class_date between ctd.use_begin_date  and  ctd.use_end_date 
    and concat(ctudd.class_date,' ',ctd.evening_begin_time)>= sla.begin_time
    and concat(ctudd.class_date,' ',ctd.evening_begin_time)<= sla.end_time
groupby ctudd.class_date,ctudd.class_id),tempas(selectcoalesce(A.class_date,B.class_date,C.class_date)AS class_date,coalesce(A.class_id,B.class_id,C.class_id)AS class_id,
    nvl(A.morning_leave_count,0)as morning_leave_count,
    nvl(B.afternoon_leave_count,0)as afternoon_leave_count,
    nvl(C.evening_leave_count,0)as evening_leave_count
from A 
    fulljoin B on A.class_date = B.class_date and A.class_id = B.class_id
    fulljoin C on A.class_date = C.class_date and A.class_id = C.class_id)select 
    class_date,
    class_id,sum(morning_leave_count)as morning_leave_count,sum(afternoon_leave_count)as afternoon_leave_count,sum(evening_leave_count)as evening_leave_count,
    substr(class_date,1,4)as yearinfo,
    substr(class_date,6,2)as monthinfo,
    substr(class_date,9,2)as dayinfo
fromtempgroupby class_date,class_id;

将结果灌入到DWM层即可

set hive.auto.convert.join=false;--分区SET hive.exec.dynamic.partition=true;SET hive.exec.dynamic.partition.mode=nonstrict;set hive.exec.max.dynamic.partitions.pernode=10000;set hive.exec.max.dynamic.partitions=100000;set hive.exec.max.created.files=150000;--hive压缩set hive.exec.compress.intermediate=true;set hive.exec.compress.output=true;--写入时压缩生效set hive.exec.orc.compression.strategy=COMPRESSION;--分桶--set hive.enforce.bucketing=true;--set hive.enforce.sorting=true;--set hive.optimize.bucketmapjoin = true;--set hive.auto.convert.sortmerge.join=true;--set hive.auto.convert.sortmerge.join.noconditionaltask=true;--并行执行set hive.exec.parallel=true;set hive.exec.parallel.thread.number=8;--小文件合并-- set mapred.max.split.size=2147483648;-- set mapred.min.split.size.per.node=1000000000;-- set mapred.min.split.size.per.rack=1000000000;--矢量化查询set hive.vectorized.execution.enabled=true;--关联优化器set hive.optimize.correlation=true;--读取零拷贝set hive.exec.orc.zerocopy=true;--join数据倾斜set hive.optimize.skewjoin=false;-- set hive.skewjoin.key=100000;set hive.optimize.skewjoin.compiletime=false;set hive.optimize.union.remove=false;-- group倾斜set hive.groupby.skewindata=false;with A as(select
    ctudd.class_date,
    ctudd.class_id,count(distinct sla.student_id)as morning_leave_count
from(select*from itcast_ods.student_leave_apply_ods where audit_state =1and cancel_state =0and valid_state =1) sla
    join itcast_dimen.class_time_dimen ctd on sla.class_id = ctd.class_id
    join(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd on ctudd.class_id = sla.class_id
where ctudd.class_date between ctd.use_begin_date  and  ctd.use_end_date 
    and concat(ctudd.class_date,' ',ctd.morning_begin_time)>= sla.begin_time
    and concat(ctudd.class_date,' ',ctd.morning_begin_time)<= sla.end_time
groupby ctudd.class_date,ctudd.class_id),
    B AS(select
    ctudd.class_date,
    ctudd.class_id,count(distinct sla.student_id)as afternoon_leave_count
from(select*from itcast_ods.student_leave_apply_ods where audit_state =1and cancel_state =0and valid_state =1) sla
    join itcast_dimen.class_time_dimen ctd on sla.class_id = ctd.class_id
    join(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd on ctudd.class_id = sla.class_id
where ctudd.class_date between ctd.use_begin_date  and  ctd.use_end_date 
    and concat(ctudd.class_date,' ',ctd.afternoon_begin_time)>= sla.begin_time
    and concat(ctudd.class_date,' ',ctd.afternoon_begin_time)<= sla.end_time
groupby ctudd.class_date,ctudd.class_id),
    C AS(select
    ctudd.class_date,
    ctudd.class_id,count(distinct sla.student_id)as evening_leave_count
from(select*from itcast_ods.student_leave_apply_ods where audit_state =1and cancel_state =0and valid_state =1) sla
    join itcast_dimen.class_time_dimen ctd on sla.class_id = ctd.class_id
    join(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd on ctudd.class_id = sla.class_id
where ctudd.class_date between ctd.use_begin_date  and  ctd.use_end_date 
    and concat(ctudd.class_date,' ',ctd.evening_begin_time)>= sla.begin_time
    and concat(ctudd.class_date,' ',ctd.evening_begin_time)<= sla.end_time
groupby ctudd.class_date,ctudd.class_id),tempas(selectcoalesce(A.class_date,B.class_date,C.class_date)AS class_date,coalesce(A.class_id,B.class_id,C.class_id)AS class_id,
    nvl(A.morning_leave_count,0)as morning_leave_count,
    nvl(B.afternoon_leave_count,0)as afternoon_leave_count,
    nvl(C.evening_leave_count,0)as evening_leave_count
from A 
    fulljoin B on A.class_date = B.class_date and A.class_id = B.class_id
    fulljoin C on A.class_date = C.class_date and A.class_id = C.class_id)insertintotable itcast_dwm.class_leave_dwm partition(yearinfo,monthinfo,dayinfo)select 
    class_date,
    class_id,sum(morning_leave_count)as morning_leave_count,sum(afternoon_leave_count)as afternoon_leave_count,sum(evening_leave_count)as evening_leave_count,
    substr(class_date,1,4)as yearinfo,
    substr(class_date,6,2)as monthinfo,
    substr(class_date,9,2)as dayinfo
fromtempgroupby class_date,class_id;
  • 旷课人数表统计
计算规则: 
    旷课人数 = 当日在读人数 - 出勤人数 - 请假人数

SQL最终实现

insertintotable itcast_dwm.class_truant_dwm partition(yearinfo,monthinfo,dayinfo)select 
    ctudd.class_date as dateinfo,
    ctudd.class_id,
    cssc.studying_student_count - nvl(cad.morning_att_count,0)- nvl(cld.morning_leave_count,0)as morning_truant_count,
    cssc.studying_student_count - nvl(cad.afternoon_att_count,0)- nvl(cld.afternoon_leave_count,0)as afternoon_truant_count,
    cssc.studying_student_count - nvl(cad.evening_att_count,0)- nvl(cld.evening_leave_count,0)as evening_truant_count,
    substr(ctudd.class_date,1,4)as yearinfo,
    substr(ctudd.class_date,6,2)as monthinfo,
    substr(ctudd.class_date,9,2)as dayinfo
from(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd 
    leftjoin itcast_dimen.class_studying_student_count_dimen cssc on  ctudd.class_date = cssc.studying_date and ctudd.class_id = cssc.class_id
    leftjoin itcast_dwm.class_attendance_dwm cad on ctudd.class_id = cad.class_id and ctudd.class_date = cad.dateinfo
    leftjoin itcast_dwm.class_leave_dwm cld on ctudd.class_id = cld.class_id and ctudd.class_date = cld.dateinfo
where ctudd.class_date in('2019-09-03','2019-09-04','2019-09-05')
  • 汇总表
insertintotable itcast_dwm.class_all_dwm partition(yearinfo,monthinfo,dayinfo)select
    ctudd.class_date as dateinfo,
    ctudd.class_id,
    cssc.studying_student_count,
    
    cad.morning_att_count,
    concat(round(nvl(cad.morning_att_count,0)/  cssc.studying_student_count *100,2),'%')as morning_att_ratio,
    cad.afternoon_att_count,
    concat(round(nvl(cad.afternoon_att_count,0)/  cssc.studying_student_count *100,2),'%')as afternoon_att_ratio,
    cad.evening_att_count,
    concat(round(nvl(cad.evening_att_count,0)/  cssc.studying_student_count *100,2),'%')as evening_att_ratio,
    
    cad.morning_late_count,
    concat(round(nvl(cad.morning_late_count,0)/  cssc.studying_student_count *100,2),'%')as morning_late_ratio,
    cad.afternoon_late_count,
    concat(round(nvl(cad.afternoon_late_count,0)/  cssc.studying_student_count *100,2),'%')as afternoon_late_ratio,
    cad.evening_late_count,
    concat(round(nvl(cad.evening_late_count,0)/  cssc.studying_student_count *100,2),'%')as evening_late_ratio,
    
    cld.morning_leave_count,
    concat(round(nvl(cld.morning_leave_count,0)/  cssc.studying_student_count *100,2),'%')as morning_leave_ratio,
    cld.afternoon_leave_count,
    concat(round(nvl(cld.afternoon_leave_count,0)/  cssc.studying_student_count *100,2),'%')as afternoon_leave_ratio,
    cld.evening_leave_count,
    concat(round(nvl(cld.evening_leave_count,0)/  cssc.studying_student_count *100,2),'%')as evening_leave_ratio,
    
    ctd.morning_truant_count,
    concat(round(nvl(ctd.morning_truant_count,0)/  cssc.studying_student_count *100,2),'%')as morning_truant_ratio,
    ctd.afternoon_truant_count,
    concat(round(nvl(ctd.afternoon_truant_count,0)/  cssc.studying_student_count *100,2),'%')as afternoon_truant_ratio,
    ctd.evening_truant_count,
    concat(round(nvl(ctd.evening_truant_count,0)/  cssc.studying_student_count *100,2),'%')as evening_truant_ratio,
    
    substr(ctudd.class_date,1,4)as yearinfo,
    substr(ctudd.class_date,6,2)as monthinfo,
    substr(ctudd.class_date,9,2)as dayinfo
    

from(select*from itcast_dimen.course_table_upload_detail_dimen where nvl(content,'')!=''and content !='开班典礼') ctudd
    leftjoin itcast_dimen.class_studying_student_count_dimen cssc on cssc.class_id = ctudd.class_id and cssc.studying_date = ctudd.class_date
    leftjoin itcast_dwm.class_attendance_dwm cad on ctudd.class_id = cad.class_id and ctudd.class_date = cad.dateinfo
    leftjoin itcast_dwm.class_leave_dwm cld on ctudd.class_id = cld.class_id and ctudd.class_date = cld.dateinfo
    leftjoin itcast_dwm.class_truant_dwm ctd on ctudd.class_id = ctd.class_id and ctudd.class_date = ctd.dateinfo
where ctudd.class_date in('2019-09-03','2019-09-04','2019-09-05');

2.7 数据的统计分析(DWS)

DWS: 细化维度统计操作

需求一: 统计每年 每月 每天 上午, 下午, 晚自习 各个班级的 相关的指标  (指的DWM层的汇总表数据)
省略....
需求二: 统计每年 每月 上午, 下午, 晚自习 各个班级的 相关的指标

需求二: 统计每年 上午, 下午, 晚自习 各个班级的 相关的指标

SQL实现

insertintotable itcast_dws.class_attendance_dws partition(yearinfo,monthinfo,dayinfo)select 
    concat(yearinfo,'-',monthinfo)as dateinfo,
    class_id,sum(studying_student_count)as studying_student_count,sum(morning_att_count)as  morning_att_count,
    concat(round(sum(morning_att_count)/sum(studying_student_count)*100,2),'%')as morning_att_ratio,sum(afternoon_att_count)as  afternoon_att_count,
    concat(round(sum(afternoon_att_count)/sum(studying_student_count)*100,2),'%')as afternoon_att_ratio,sum(evening_att_count)as evening_att_count,
    concat(round(sum(evening_att_count)/sum(studying_student_count)*100,2),'%')as evening_att_ratio,sum(morning_late_count)as  morning_late_count,
    concat(round(sum(morning_late_count)/sum(studying_student_count)*100,2),'%')as morning_late_ratio,sum(afternoon_late_count)as  afternoon_late_count,
    concat(round(sum(afternoon_late_count)/sum(studying_student_count)*100,2),'%')as afternoon_late_ratio,sum(evening_late_count)as evening_late_count,
    concat(round(sum(evening_late_count)/sum(studying_student_count)*100,2),'%')as evening_late_ratio,sum(morning_leave_count)as  morning_leave_count,
    concat(round(sum(morning_leave_count)/sum(studying_student_count)*100,2),'%')as morning_leave_ratio,sum(afternoon_leave_count)as  afternoon_leave_count,
    concat(round(sum(afternoon_leave_count)/sum(studying_student_count)*100,2),'%')as afternoon_leave_ratio,sum(evening_leave_count)as evening_leave_count,
    concat(round(sum(evening_leave_count)/sum(studying_student_count)*100,2),'%')as evening_leave_ratio,sum(morning_truant_count)as  morning_truant_count,
    concat(round(sum(morning_truant_count)/sum(studying_student_count)*100,2),'%')as morning_truant_ratio,sum(afternoon_truant_count)as  afternoon_truant_count,
    concat(round(sum(afternoon_truant_count)/sum(studying_student_count)*100,2),'%')as afternoon_truant_ratio,sum(evening_truant_count)as evening_truant_count,
    concat(round(sum(evening_truant_count)/sum(studying_student_count)*100,2),'%')as evening_truant_ratio,'4'as  time_type,
    yearinfo,
    monthinfo,'-1'as dayinfo
    
from  itcast_dwm.class_all_dwm
groupby yearinfo,monthinfo,class_id;
标签: python 大数据 java

本文转载自: https://blog.csdn.net/qq_45588318/article/details/127693159
版权归原作者 呆猛的呆呆小哥 所有, 如有侵权,请联系我们删除。

“Python+大数据-知行教育(七)-学生出勤主题看板”的评论:

还没有评论