写在前面:我是「云祁」,一枚热爱技术、会写诗的大数据开发猿。昵称来源于王安石诗中一句
[ 云之祁祁,或雨于渊 ]
,甚是喜欢。
写博客一方面是对本身学习的一点点总结及记录,另外一方面则是但愿可以帮助更多对大数据感兴趣的朋友。若是你也对数据中台、数据建模、数据分析以及Flink/Spark/Hadoop/数仓开发
感兴趣,能够关注个人动态 ,让咱们一块儿挖掘大数据的价值~
天天都要进步一点点,生命不是要超越别人,而是要超越本身! (ง •_•)ง
面试
师兄在面试时遇到了这条SQL题,回来我帮他参谋了下,以为很是有意思,让咱们一块儿来看看这道差点吊打师兄的笔试题吧!🤒sql
对方给了两张表(分别是:派工记录表和打卡记录表),以及一张需求表(须要咱们写查询语句得出),内容以下:数据库
一、表示某人从某日开始到某日结束,按要求工做,派工期间每日打卡时间必须在“要求到岗时间”前(含要求时间,精确到分钟),不然迟到。函数
例如:oop
要求7:00,则6:59或者7:00:59 都不算迟到;7:01则视为迟到1分钟学习
二、行1中“派工结束日期”为null,表示此人的工做结束时间还没有肯定,还在搬砖中;
行2中派工结束日期为2020-02-15,表示派工于02-15日结束。大数据
三、假设员工名字不重复,每人只有一条派工信息3d
建表语句以下:code
create table work_plan ( worker_name varchar(10), --人员 start_date date, --派工起始日 end_date date, --派工截止日 sign_time varchar(10) --派工期间的须要打卡时间 ) insert into work_plan values ('张三','2020-01-01',null,'06:30'), ('李四','2020-02-01','2020-02-15','07:00'), ('王五','2019-12-29','2020-03-30','06:00'), ('赵六','2019-12-29','2020-03-30','06:00')
在员工每次按指纹考勤时
都会生成一条记录orm
建表语句以下:
create table sign_log ( worker_name varchar(10), sign_time datetime ) insert into sign_log values ('张三','2020-02-16 04:01'), ('张三','2020-02-16 05:02'), ('张三','2020-02-16 06:03'), ('王五','2020-02-16 07:03'), ('王五','2020-02-16 08:03'), ('王五','2020-02-16 09:03')
写一个查询语句,输入参数:日期(date),输出表格以下:
注:
一、2020-02-16李四派工期已结束,不在派工期间不须要计算考勤,故不用显示
二、赵六在当日没有打卡,按照迟到算,迟到时间1440分钟
咱们要最终获得迟到时间,须要用表二中最先的打卡时间减去表一中要求的截至打卡时间,但很明显两个表日期的数据格式不一致,对表一的时间数据 sing_time 咱们须要和年份进行拼接,而后再与表二的打卡时间进行比较,便可得出最终咱们须要的迟到时间。😑
但在实际写查询语句时,我发现那样会过于繁琐,变考虑将上述步骤经过 SQL 中自定义函数来实现。先来复习下MySQL中的自定义函数。
自定义函数实例:
先来一个简单的,建立一个函数将'2009-06-23 00:00:00'
这样格式的datetime时间转化为'2009年6月23日0时0分0秒'
这样的格式:
DELIMITER $$ DROP FUNCTION IF EXISTS `sp_test`.`getdate`$$ CREATE FUNCTION `sp_test`.`getdate`(gdate datetime) RETURNS varchar(255) BEGIN DECLARE x VARCHAR(255) DEFAULT ''; SET x= date_format(gdate,'%Y年%m月%d日%h时%i分%s秒'); RETURN x; END $$ DELIMITER ;
解析:
第一句是定义一个结束标识符,由于MySQL默认是以分号做为SQL语句的结束符的,而函数体内部要用到分号,因此会跟默认的SQL结束符发生冲突,因此须要先定义一个其余的符号做为SQL的结束符;
第二句是若是这个函数已经存在了,就删除掉,sp_test是数据库的名字,函数是跟数据库相关联的,getdate是函数的名字;
第三句是建立一个函数,()里是参数的名字和类型,RETURNS 定义这个函数返回值的类型;
函数体必须放在BEGIN END之间;
DECLARE 是定义函数体的变量,这里定义一个变量x,默认是空,而后SET给x变量赋值;
RETURN 是返回值,这里把变量x返回,x的类型必须与第三句中定义的返回类型一致。
调用:
SELECT getdate('2009-06-23 00:00:00');
返回 '2009年06月23日00时00分00秒'
咱们先来完成时间处理的自定义函数,代码以下:
DELIMITER $$ DROP FUNCTION IF EXISTS func_date_sub $$ -- d1 6:00 d2 '2020-2-16 4:5:0' CREATE FUNCTION func_date_sub(d1 VARCHAR(20),d2 DATETIME) RETURNS INT BEGIN IF d2 IS NULL THEN RETURN -1440; ELSE RETURN CEIL((UNIX_TIMESTAMP(CONCAT('2020-2-16 ',d1))-UNIX_TIMESTAMP(d2))/60); END IF; END $$ DELIMITER ;
SQL 查询语句以下:
select res.worker_name,res.attend,if(res.latetime<0,'是','否') as isLate,if( res.latetime<0,abs(res.latetime),0) as latetime from (select ck.worker_name,ck.attend,func_date_sub(ck.sign_time,ck.st) latetime from (select e.worker_name,e.sign_time,'2020-02-16' as attend,k.st from (select * from work_plan where datediff(end_date,'2020-2-16')>0 or end_date is null) e left join (select worker_name,min(sign_time) st from sign_log group by worker_name) k on e.worker_name = k.worker_name) ck)res;
最终结果仍是很是完美的,若是小伙伴有更好的意见,欢迎留言讨论~