在這里,我們要做一個簡單的員工考勤記錄查詢系統的后台數據庫。業務需求如下所示:
1.統計每天來的最早、來的最晚、走的最早、走得最晚的人的姓名
1.1 統計每天來得最早的人的姓名和打卡時間
步驟1:從考勤信息表中查詢出每天來得最早的人的上班打卡時間和人員編號
解決這個問題的時候本來考慮的是在考勤信息記錄表中按照日期對考勤信息進行分組,然后取每組中上班時間(att_work_datatime)的最小值,但是后來幾經折騰發現group by只能實現分組,而order by只能實現組外排序,因此這個方法只能放棄。再三考慮了一下,可以在分組之前先對表中att_work_datatime列中的所有值進行升序排序后生成一個臨時表,然后對這個臨時表中的att_work_datatime按照日期再分組,這樣對att_work_datatime列按照日期group by之后取的就是每天上班打卡最早的人,我們從attendance_info_table(考勤信息表)表中查詢出出每天上班時間最早的人的編號、上班時間和下班時間,sql語句如下:
select tmp.att_work_datatime,tmp.after_work_datatime,tmp.emp_id
from
(select id,att_work_datatime,after_work_datatime,emp_id
from attendance_info_table
order by att_work_datatime) as tmp
group by Date(att_work_datatime)
執行查詢結果如下:

結果並不如願,發現多了一行null值,才發現分組的時候mysql默認將null分為一組,這樣就多了一組null數據。這樣我們只需要在排序之前過濾掉 打卡時間為null的數據行,sql語句如下:
select tmp.att_work_datatime,tmp.after_work_datatime,tmp.emp_id
from
(select id,att_work_datatime,after_work_datatime,emp_id
from attendance_info_table
where att_work_datatime is not null
order by att_work_datatime) as tmp
group by Date(att_work_datatime)
查詢出來的結果如下圖所示:

步驟2:從員工表和考勤信息表中聯結查詢出每天來得最早的人的姓名上班打卡時間
這樣,還沒有滿足需求,我們要打印的是每天來得最早的人的姓名和上班打卡時間,由於員工的信息在另外一張表employee_info_table中放着,這樣我們需要用到多表聯結查詢,根據雇員編號進行等值聯結查詢,sql語句如下所示:
select em.emp_name,tmp.att_work_datatime
from employee_info_table as em ,
(select id,att_work_datatime,after_work_datatime,emp_id
from attendance_info_table
where att_work_datatime is not null
order by att_work_datatime )
as tmp
where em.id=tmp.emp_id
group by Date(att_work_datatime)
from employee_info_table as em ,
(select id,att_work_datatime,after_work_datatime,emp_id
from attendance_info_table
where att_work_datatime is not null
order by att_work_datatime )
as tmp
where em.id=tmp.emp_id
group by Date(att_work_datatime)
select eit.*, ait.att_work_datatimefrom attendance_info_table ait, employee_info_table eitwhere ait.emp_id = eit.id and ait.att_work_datatime in(select min(att_work_datatime)from attendance_info_tablewhere att_work_datatime is not nullgroup by Date(att_work_datatime))order by ait.att_work_datatime asc;
查詢出來的結果如下圖所示:

OK,大功告成。在這里要說明的是,為了縮短sql語句並且為了在一條sql語句中多次使用相同的表,上面的查詢中我們使用em 作為員工信息表employee_info_table 的表別名,使用tmp作為排序后生成的臨時表的表別名。
1.2 統計每天來得最晚的人的姓名和打卡時間
步驟和2.1中統計每天來的最早的人的方法相同,唯一不同的是分組之前先對表中att_work_datatime列中的所有值進行降序排序,sql語句如下:
select em.emp_name,tmp.att_work_datatime
from employee_info_table as em ,(select id,att_work_datatime,after_work_datatime,emp_id
from attendance_info_table
where att_work_datatime is not null
order by att_work_datatime desc)
as tmp
where em.id=tmp.emp_id
group by Date(att_work_datatime)
select eit.*, ait.att_work_datatimefrom attendance_info_table ait, employee_info_table eitwhere ait.emp_id = eit.id and ait.att_work_datatime in(select max(att_work_datatime)from attendance_info_tablewhere att_work_datatime is not nullgroup by Date(att_work_datatime))order by ait.att_work_datatime asc;
執行查詢的結果如下:

1.3 統計每天走得最早的人的姓名和打卡時間
步驟和2.1中統計每天來的最早的人的方法相同,唯一不同的是對表中列中after_work_datatime的所有值進行升 序排序,並將查詢的列由att_work_datatime改為after_work_datatime,sql語句如下:
select em.emp_name,tmp.after_work_datatime
from employee_info_table as em ,(select id,att_work_datatime,after_work_datatime,emp_id
from attendance_info_table
where after_work_datatime is not null
order by after_work_datatime)
as tmp
where em.id=tmp.emp_id
group by Date(after_work_datatime)
select eit.*, ait.after_work_datatimefrom attendance_info_table ait, employee_info_table eitwhere ait.emp_id = eit.id and ait.after_work_datatime in(select min(after_work_datatime)from attendance_info_tablewhere after_work_datatime is not nullgroup by Date(after_work_datatime))order by ait.after_work_datatime asc;
查詢結果如下:
(select id,att_work_datatime,after_work_datatime,emp_id
from attendance_info_table
where after_work_datatime is not null
order by after_work_datatime desc)
as tmp
where em.id=tmp.emp_id
group by Date(after_work_datatime)

1.4 統計每天走得最晚的人的姓名和打卡時間
步驟和2.2中統計每天來的最晚的人的方法相同,唯一不同的是對表中列中after_work_datatime的所有值進行降序排序,並將查詢的列由att_work_datatime改為after_work_datatime,sql語句如下:
select em.emp_name,tmp.after_work_datatime
from employee_info_table as em ,(select id,att_work_datatime,after_work_datatime,emp_id
from attendance_info_table
where after_work_datatime is not null
order by after_work_datatime desc)
as tmp
where em.id=tmp.emp_id
group by Date(after_work_datatime)
select eit.*, ait.after_work_datatimefrom attendance_info_table ait, employee_info_table eitwhere ait.emp_id = eit.id and ait.after_work_datatime in(select max(after_work_datatime)from attendance_info_tablewhere after_work_datatime is not nullgroup by Date(after_work_datatime))order by ait.after_work_datatime asc;
查詢結果如下:

2.統計每天工作時間最長、工作時間最短的人的姓名
2.1統計每天工作時間最長的人的姓名
步驟1:從考勤信息表中查詢出每天工作時間最長的人的編號和工作時長
解決這個問題,我們需要建立在問題1解決方法的基礎上,我們先取出考勤信息表中上班打卡時間att_work_datatime、下班打卡時 間after_work_datatime、上下班打卡時間之差作為一天的工作時長att_time以及員工編號emp_id生成一個臨時表tmp並將打卡時間為null的數據過濾掉,然后對tmp表中的att_time進行降序排 序然后根據日期進行分組,這樣我們就可以從attendance_info_table(考勤信息表)表中查詢出每天工作時間最長的人的編號和此人的工作時長。為了計算兩個時間差,我們使用mysql自帶的函數timediff(time1,time2)來計算time1-time2的時長。sql語句如下:
select tmp.att_time,tmp.emp_id
from
(select id,att_work_datatime,after_work_datatime,timediff(after_work_datatime,att_work_datatime) as att_time,emp_id
from attendance_info_table
where att_work_datatime is not null and after_work_datatime is not null
order by att_time desc) as tmp
group by Date(att_work_datatime)
查詢出的結果如下圖所示:

步驟2:從考勤信息表和員工表中利用等值聯結查詢出每天工作時間最長的人的姓名和工作時長
我們根據雇員編號進行等值聯結查詢出每天工作時間最長的人的姓名和工作時長,sql語句如下所示:
select eit.*,tmp.att_time, tmp.att_work_datatimefrom employee_info_table eit,(select id,att_work_datatime,timediff(after_work_datatime,att_work_datatime) as att_time,emp_idfrom attendance_info_tablewhere att_work_datatime is not null and after_work_datatime is not null) as tmpwhere eit.id=tmp.emp_id and tmp.att_time in(select max(timediff(after_work_datatime,att_work_datatime)) as att_timefrom attendance_info_tablewhere att_work_datatime is not null and after_work_datatime is not nullgroup by date(att_work_datatime))group by date(tmp.att_work_datatime)order by att_work_datatime;執行出的結果如下圖所示:

2.2統計每天工作時間最短的人的姓名
select eit.*,tmp.att_time, tmp.att_work_datatimefrom employee_info_table eit,(select id,att_work_datatime,timediff(after_work_datatime,att_work_datatime) as att_time,emp_idfrom attendance_info_tablewhere att_work_datatime is not null and after_work_datatime is not null) as tmpwhere eit.id=tmp.emp_id and tmp.att_time in(select min(timediff(after_work_datatime,att_work_datatime)) as att_timefrom attendance_info_tablewhere att_work_datatime is not null and after_work_datatime is not nullgroup by date(att_work_datatime))group by date(tmp.att_work_datatime)order by att_work_datatime;執行結果如下所示:

3.統計每天遲到的人數、早退的人數
3.1統計每天遲到的人數
select date(att_work_datatime) as date,count(*) as late_numsfrom attendance_info_tablewhere timediff(time(att_work_datatime),'09:30:59') > 0 and att_work_datatime is not nullgroup by date(att_work_datatime)
執行結果如下圖所示:

3.2統計每天早退的人數
select date(after_work_datatime) as date,count(*) as leave_early_numsfrom attendance_info_tablewhere after_work_datatime is not nulland timediff(time(after_work_datatime),'18:00:00')<0or timediff(after_work_datatime,att_work_datatime)<'08:00:00'group by date(after_work_datatime)執行結果如下圖所示:

4.統計每個月遲到的人按遲到次數降序排序
select eit.*, count(*) as late_numsfrom attendance_info_table as ait,employee_info_table as eitwhere ait.att_work_datatime is not nulland timediff(time(ait.att_work_datatime),'09:30:59') > 0and ait.emp_id = eit.idgroup by emp_idorder by late_nums desc;
執行結果如下:

5.統計出遲到的人並按姓名按升序排序,打印出遲到的時間
select eit.*,timediff(time(ait.att_work_datatime),'09:30:59') as lately_times,date(ait.att_work_datatime) as lately_datefrom attendance_info_table as ait,employee_info_table as eitwhere ait.att_work_datatime is not nulland timediff(time(ait.att_work_datatime),'09:30:59') > 0and eit.id=ait.emp_idorder by eit.emp_name asc;執行結果如下:![]()
6.公司規定:每遲到一次扣10塊錢,每分鍾扣1塊錢,計算出每天遲到的人扣的錢和公司一天因為遲到扣的錢的總數
6.1計算出每天遲到的人扣的錢
select eit.*,timediff(time(ait.att_work_datatime),'09:30:59') as lately_times,date(ait.att_work_datatime) as lately_date,(10+1*(TIME_TO_SEC(timediff(time(ait.att_work_datatime),'09:30:59')))/60) as '罰金(元)'
from attendance_info_table as ait,employee_info_table as eit
where ait.att_work_datatime is not null
and timediff(time(ait.att_work_datatime),'09:30:59') > 0
and eit.id=ait.emp_id
order by eit.emp_name asc
執行結果如下:
6.1計算出公司每天因為遲到所扣的錢select tmp.lately_date,sum(tmp.fadefor) as '總罰金(元)'
from
(select eit.*,timediff(time(ait.att_work_datatime),'09:30:59') as lately_times,date(ait.att_work_datatime) as lately_date,(10+1*(TIME_TO_SEC(timediff(time(ait.att_work_datatime),'09:30:59')))/60) as fadefor
from attendance_info_table as ait,employee_info_table as eit
where ait.att_work_datatime is not null
and timediff(time(ait.att_work_datatime),'09:30:59') > 0
and eit.id=ait.emp_id
order by eit.emp_name asc) as tmp
group by tmp.lately_date執行結果如下:![]()
7.統計出每個月每個人因為遲到扣多少錢,按扣的錢數降序排序列出名單
步驟一:統計出每個人每天遲到的時間並計算每個人每天的罰金
方法同6.1
步驟二:根據人員編號進行分組,統計每個人每個月所扣的錢,並排序
select tmp.id,tmp.emp_name,sum(tmp.fadefor) as 'total_fadefor' from
(select eit.*,(10+1*(TIME_TO_SEC(timediff(time(ait.att_work_datatime),'09:30:59')))/60) as 'fadefor'
from attendance_info_table as ait,employee_info_table as eit
where ait.att_work_datatime is not null
and timediff(time(ait.att_work_datatime),'09:30:59') > 0
and eit.id=ait.emp_id) as tmp
group by tmp.id
order by total_fadefor desc;
查詢結果如下:

8.列舉出既沒有遲到也沒有早退記錄的人的名單
步驟一:統計出每個人每個月正常出勤的天數
select eit.*,count(*) as normal_nums
from attendance_info_table as ait,employee_info_table as eit
where ait.att_work_datatime is not null
and ait.after_work_datatime is not null
and timediff(time(ait.att_work_datatime),'09:30:59') < 0
and timediff(after_work_datatime,att_work_datatime)>'08:00:00'
and ait.emp_id = eit.id
group by ait.emp_id

步驟2:查詢出出勤次數大於指定天數的人的名單
select tmp.id,tmp.emp_name from
(select eit.*,count(*) as normal_nums
from attendance_info_table as ait,employee_info_table as eit
where ait.att_work_datatime is not null
and ait.after_work_datatime is not null
and timediff(time(ait.att_work_datatime),'09:30:59') < 0
and timediff(after_work_datatime,att_work_datatime)>'08:00:00'
and ait.emp_id = eit.id
group by ait.emp_id
)as tmp where tmp.normal_nums>=21
(select eit.*,count(*) as normal_nums
from attendance_info_table as ait,employee_info_table as eit
where ait.att_work_datatime is not null
and ait.after_work_datatime is not null
and timediff(time(ait.att_work_datatime),'09:30:59') < 0
and timediff(after_work_datatime,att_work_datatime)>'08:00:00'
and ait.emp_id = eit.id
group by ait.emp_id
)as tmp where tmp.normal_nums>=21

步驟三:通過獲取一個月的天數,查詢出一個月每天都正常出勤的人的名單
select tmp.id,tmp.emp_name from
(select eit.*,count(*) as normal_nums
from attendance_info_table as ait,employee_info_table as eit
where ait.att_work_datatime is not null
and ait.after_work_datatime is not null
and timediff(time(ait.att_work_datatime),'09:30:59') < 0
and timediff(after_work_datatime,att_work_datatime)>'08:00:00'
and ait.emp_id = eit.id
group by ait.emp_id
)as tmp
where tmp.normal_nums>=
(select count(*)
from
(select date(att_work_datatime) as date
from attendance_info_table
where att_work_datatime is not null
group by date(att_work_datatime)) as tmp)
(select eit.*,count(*) as normal_nums
from attendance_info_table as ait,employee_info_table as eit
where ait.att_work_datatime is not null
and ait.after_work_datatime is not null
and timediff(time(ait.att_work_datatime),'09:30:59') < 0
and timediff(after_work_datatime,att_work_datatime)>'08:00:00'
and ait.emp_id = eit.id
group by ait.emp_id
)as tmp
where tmp.normal_nums>=
(select count(*)
from
(select date(att_work_datatime) as date
from attendance_info_table
where att_work_datatime is not null
group by date(att_work_datatime)) as tmp)
執行結果如下: