師兄大廠面試遇到這條 SQL 數據分析題,差點含淚而歸!

寫在前面:我是「雲祁」,一枚熱愛技術、會寫詩的大數據開發猿。暱稱來源於王安石詩中一句 [ 雲之祁祁,或雨於淵 ] ,甚是喜歡。


寫博客一方面是對本身學習的一點點總結及記錄,另外一方面則是但願可以幫助更多對大數據感興趣的朋友。若是你也對 數據中臺、數據建模、數據分析以及Flink/Spark/Hadoop/數倉開發 感興趣,能夠關注個人動態 ,讓咱們一塊兒挖掘大數據的價值~


天天都要進步一點點,生命不是要超越別人,而是要超越本身! (ง •_•)ง 面試

1、背景

師兄在面試時遇到了這條SQL題,回來我幫他參謀了下,以爲很是有意思,讓咱們一塊兒來看看這道差點吊打師兄的筆試題吧!🤒sql

對方給了兩張表(分別是:派工記錄表和打卡記錄表),以及一張需求表(須要咱們寫查詢語句得出),內容以下:數據庫

2、派工記錄表

一、表示某人從某日開始到某日結束,按要求工做,派工期間每日打卡時間必須在「要求到崗時間」前(含要求時間,精確到分鐘),不然遲到。函數

例如: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')

在這裏插入圖片描述

3、打卡記錄表

在員工每次按指紋考勤時
都會生成一條記錄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')

在這裏插入圖片描述

4、需求

寫一個查詢語句,輸入參數:日期(date),輸出表格以下:

在這裏插入圖片描述
注:
一、2020-02-16李四派工期已結束,不在派工期間不須要計算考勤,故不用顯示
二、趙六在當日沒有打卡,按照遲到算,遲到時間1440分鐘

5、思路

咱們要最終獲得遲到時間,須要用表二中最先的打卡時間減去表一中要求的截至打卡時間,但很明顯兩個表日期的數據格式不一致,對錶一的時間數據 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秒'

6、最終答案

咱們先來完成時間處理的自定義函數,代碼以下:

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;

在這裏插入圖片描述

最終結果仍是很是完美的,若是小夥伴有更好的意見,歡迎留言討論~

相關文章
相關標籤/搜索