[SQL]找出本月全勤的人員報表

[SQL]找出本月全勤的人員報表

前言

前陣子遇到要找出"本月全勤的人員清單",格式為:部門 員工數 員工名單(以逗號分開),如部門1  3 何大牌,郭小玉,林大仔。

以下是實作過程整理。

資料準備

開始前,先準備部門檔、員工檔及請假檔,並加入測試資料。


--部門檔
CREATE TABLE DEPT
(
    DEPT_ID VARCHAR(32) NOT NULL PRIMARY KEY
    ,DEPT_NAME NVARCHAR(64)
)
--初始資料 (新增99個部門)
DELETE FROM DEPT
DECLARE @I INT
SET @I = 1
WHILE @I < 100
BEGIN
    INSERT INTO DEPT(DEPT_ID, DEPT_NAME) 
    VALUES('DEPT_ID:' + RIGHT('0' + LTRIM(STR(@I)),  2), 'DEPT_NAME:' + RIGHT('0' + LTRIM(STR(@I)),  2))
    SET @I = @I + 1
END
SELECT * 
FROM DEPT
ORDER BY DEPT_ID

--員工檔
CREATE TABLE EMP
(
EMP_NO VARCHAR(32) NOT NULL PRIMARY KEY
,EMP_NAME NVARCHAR(64)
,DEPT_ID VARCHAR(32)
)

--初始資料 (各部門新增10個人)
delete from EMP
DECLARE @I INT, @J INT, @K INT
SET @I = 1
SET @J = 1
WHILE @I < 100
BEGIN
    SET @K = 1
    WHILE @K < 11
    BEGIN
        INSERT INTO EMP(EMP_NO, EMP_NAME, DEPT_ID) 
        VALUES('EMP_NO:' +  RIGHT('0000' + LTRIM(STR(@J)),  4), 'EMP_NAME:' +  RIGHT('0000' + LTRIM(STR(@J)),  4) 
        , 'DEPT_ID:' + RIGHT('0' + LTRIM(STR(@I)),  2))
        SET @J = @J + 1
        SET @K = @K + 1
    END
    SET @I = @I + 1
END
SELECT * FROM EMP 
ORDER BY EMP_NO

--請假檔
CREATE TABLE LEAVE
(
    SEQ_NO INT IDENTITY(1,1) NOT NULL PRIMARY KEY
    ,EMP_NO VARCHAR(32)
    ,LEAVE_START_DATE DATETIME
    ,LEAVE_END_DATE DATETIME
)

--初始資料
DELETE FROM LEAVE
DECLARE @I INT, @J INT, @K INT
SET @I = 1
SET @J = 1
WHILE @I < 100
BEGIN
    SET @K = 1
    WHILE @K < 11
    BEGIN
        IF (@K % 2 = 0 AND @J % 7 <> 0)
        BEGIN
            INSERT INTO LEAVE(EMP_NO, LEAVE_START_DATE, LEAVE_END_DATE)
            VALUES('EMP_NO:' +  RIGHT('0000' + LTRIM(STR(@J)),  4)
            , DATEADD(day, -1, GETDATE()), DATEADD(day, -1, GETDATE()))
        END 
        ELSE
        BEGIN
            -- DO NOTHING
            PRINT '@K:' + STR(@K)
        END
        SET @J = @J + 1
        SET @K = @K + 1
    END

    SET @I = @I + 1
END
SELECT * FROM LEAVE 
ORDER BY EMP_NO

實作

建好資料後,就可以開始練習了! 我的習慣是先一步一步拆解,各部份都解出來後,再組起來。

  • 先找出本月的方式

DATEDIFF(MONTH, GETDATE(), DATA日期) = 0 AND  DATEDIFF(YEAR, GETDATE(), DATA日期) = 0 或是 YEAR = YEAR(DATA日期) AND MONTH = MONTH(DATA日期)

  • 找出本月有請假的員工

DECLARE @CUR_MONTH INT, @CUR_YEAR INT
SET @CUR_MONTH = MONTH(GETDATE())
SET @CUR_YEAR = YEAR(GETDATE())
SELECT A.*
FROM LEAVE A (NOLOCK)
WHERE (YEAR(A.LEAVE_START_DATE) = @CUR_YEAR AND MONTH(A.LEAVE_START_DATE) = @CUR_MONTH) 
OR (YEAR(A.LEAVE_END_DATE) = @CUR_YEAR AND MONTH(A.LEAVE_END_DATE) = @CUR_MONTH) 
  • 找出本月沒有請假的員工(全勤),去除掉本月請假員工就是本月的全勤員工

DECLARE @CUR_MONTH INT, @CUR_YEAR INT
SET @CUR_MONTH = MONTH(GETDATE())
SET @CUR_YEAR = YEAR(GETDATE())

--本月沒請假的員工
SELECT B.DEPT_ID, B.DEPT_NAME, A.EMP_NO, A.EMP_NAME 
FROM EMP A (NOLOCK) INNER JOIN DEPT B (NOLOCK)
ON (A.DEPT_ID = B.DEPT_ID)
WHERE A.EMP_NO 
NOT IN
(
    --本月有請假的員工
    SELECT A.EMP_NO
    FROM LEAVE A (NOLOCK)
    WHERE (YEAR(A.LEAVE_START_DATE) = @CUR_YEAR AND MONTH(A.LEAVE_START_DATE) = @CUR_MONTH) 
    OR (YEAR(A.LEAVE_END_DATE) = @CUR_YEAR AND MONTH(A.LEAVE_END_DATE) = @CUR_MONTH) 
)
  • 找出本月某部門的全勤員工姓名清單(如何大牌,郭小玉,林大仔)

這個需要利用一個字串變數來串接,所以建立UFN_GET_NONLEAVE_EMPNAMELIST Function來用

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[UFN_GET_NONLEAVE_EMPNAMELIST]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[UFN_GET_NONLEAVE_EMPNAMELIST]
GO
CREATE FUNCTION [dbo].[UFN_GET_NONLEAVE_EMPNAMELIST]
/****************************************************************************************
'程式代號:UFN_GET_EMP_NAME_LIST
'程式名稱:傳入年、月、部門代號,取得該年月,該部門的全勤的員工姓名LIST
'參數說明:@intYear:年, @intMonth:月, @strDeptId:部門代號
'傳回值 :NVARCHAR:員工姓名LIST
'副作用 :無
'備 註 :所有參數皆必需傳入
'範 例 :SELECT dbo.UFN_GET_NONLEAVE_EMPNAMELIST(2010, 02, 'DEPT_ID:01')         
****************************************************************************************/ 
(@intYear INT
, @intMonth INT
, @strDeptId VARCHAR(64))
RETURNS NVARCHAR(MAX)
BEGIN
        DECLARE @EMP_NAME_LIST NVARCHAR(MAX)
        SET @EMP_NAME_LIST = ''
        SELECT @EMP_NAME_LIST = @EMP_NAME_LIST + A.EMP_NAME + ','
        FROM EMP A (NOLOCK) INNER JOIN DEPT B (NOLOCK)
        ON (B.DEPT_ID = @strDeptId AND A.DEPT_ID = B.DEPT_ID)
        WHERE A.EMP_NO 
        NOT IN
        (
            --本月有請假的員工
            SELECT A.EMP_NO
            FROM LEAVE A (NOLOCK)
            WHERE (YEAR(A.LEAVE_START_DATE) = @intYear AND MONTH(A.LEAVE_START_DATE) = @intMonth) 
            OR (YEAR(A.LEAVE_END_DATE) = @intYear AND MONTH(A.LEAVE_END_DATE) = @intMonth) 
        )
        DECLARE @RESULT_LEN INT
        SET @RESULT_LEN = LEN(@EMP_NAME_LIST)
        IF @RESULT_LEN > 0
        BEGIN
            SET @EMP_NAME_LIST = LEFT(@EMP_NAME_LIST, @RESULT_LEN -1)
        END
        RETURN @EMP_NAME_LIST
END
  • 找出本月各部門的的全勤員工數及員工姓名清單

利用Group再加上之前寫的Function就可以達到我們最後要的結果

DECLARE @CUR_MONTH INT, @CUR_YEAR INT
SET @CUR_MONTH = MONTH(GETDATE())
SET @CUR_YEAR = YEAR(GETDATE())
PRINT '@CUR_MONTH:' + STR(@CUR_MONTH)
PRINT '@CUR_YEAR:' + STR(@CUR_YEAR)
--本月沒請假的員工
SELECT B.DEPT_ID, B.DEPT_NAME, COUNT(*) AS EMP_CNT
, dbo.UFN_GET_NONLEAVE_EMPNAMELIST(@CUR_YEAR, @CUR_MONTH, B.DEPT_ID) AS EMP_NAME_LIST 
FROM EMP A (NOLOCK) INNER JOIN DEPT B (NOLOCK)
ON (A.DEPT_ID = B.DEPT_ID)
WHERE A.EMP_NO 
NOT IN
(
    --本月有請假的員工
    SELECT A.EMP_NO
    FROM LEAVE A (NOLOCK)
    WHERE (YEAR(A.LEAVE_START_DATE) = @CUR_YEAR AND MONTH(A.LEAVE_START_DATE) = @CUR_MONTH) 
    OR (YEAR(A.LEAVE_END_DATE) = @CUR_YEAR AND MONTH(A.LEAVE_END_DATE) = @CUR_MONTH) 
)
GROUP BY B.DEPT_ID, B.DEPT_NAME

結論

這個練習主要是利用Group來算出各部門的員工人數,另外就是再透過Function來取得員工名稱清單,而Function中則是透過一個字串變數來串員工的姓名,不知是否有無更好的方式來達成呢? Thanks.

附上範例程式:找出本月全勤人員.rar

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^