用 Postgres 計算員工上下班紀錄

postgres

這應該算是一個蠻簡單的情境,公司都需要去紀錄每位員工上下班紀錄,或者是紀錄每天刷卡補助餐點,在一定的時間內刷卡才會進行公司補助,非在約定的時間點刷卡則不補助,底下看看公司可能會想要的表格紀錄。在後台頁面會進行時間區域的選擇。

起始日期: 2020-06-01 結束日期: 2020-06-30 早上時間: 08:00 ~ 09:00 晚上時間: 18:00 ~ 19:00

建立表格來紀錄,其中 testPostgresSchema

1
2
3
4
5
6
7
8
9
CREATE TABLE "test"."workshift" (
    "id" int8 NOT NULL DEFAULT nextval('workshift_id_seq'::regclass),
    "company_id" int8,
    "employee_id" int8,
    "recorded_at" timestamp NOT NULL,
    "created_at" timestamp,
    "updated_at" timestamp,
    PRIMARY KEY ("id")
);

其中 recorded_at 是員工刷卡傳送上來的時間,本篇會介紹兩種情境的寫法,用來分別紀錄每個月各別員工刷卡次數,以及單月每天員工刷卡總次數。

員工刷卡次數

輸出的表格如下

Employee IDNameBreakfastDinner
1234Mr. Wang1
4567Mr. Lee1

底下會用 postgres 內的 CASE WHEN 語法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
SELECT
    employee_id,
    sum(
        CASE WHEN to_char(recorded_at, 'hh24:mi') >= '07:00'
            AND to_char(recorded_at, 'hh24:mi') < '09:00' THEN
            1
        ELSE
            0
        END) AS breakfast_count,
    sum(
        CASE WHEN to_char(recorded_at, 'hh24:mi') >= '18:00'
            AND to_char(recorded_at, 'hh24:mi') < '19:00' THEN
            1
        ELSE
            0
        END) AS dinner_count
FROM
    "public"."workshift"
WHERE
    workshift.company_id = 1
    AND recorded_at BETWEEN '2020-07-01T00:00:00Z'
    AND '2020-07-30T00:00:00Z'
GROUP BY
    employee_id
ORDER BY
    employee_id DESC
LIMIT 50

單月統計每天資料

另一種情境會是紀錄每天有多少刷卡紀錄,來計算補助金額。輸出的結果如下:

DayBreakfastDinner
2020-07-0114
2020-07-0231

SQL 語法如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
SELECT
    to_char(recorded_at, 'YYYY-MM-DD') AS day_of_month,
    sum(
        CASE WHEN to_char(recorded_at, 'hh24:mi') >= '07:00'
            AND to_char(recorded_at, 'hh24:mi') < '09:00' THEN
            1
        ELSE
            0
        END) AS breakfast_count,
    sum(
        CASE WHEN to_char(recorded_at, 'hh24:mi') >= '18:00'
            AND to_char(recorded_at, 'hh24:mi') < '19:00' THEN
            1
        ELSE
            0
        END) AS dinner_count
FROM
    "public"."workshift"
WHERE
    workshift.company_id = 1
    AND recorded_at BETWEEN '2020-07-01T00:00:00Z'
    AND '2020-07-31T00:00:00Z'
GROUP BY
    day_of_month
ORDER BY
    day_of_month DESC
LIMIT 50

比較不一樣的地方是,透過 to_char 函式來取的每一天時間來計算所有員工刷卡次數來結算金額。搭配 GraphQL 語法搜尋會是

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
query {
  reports(
    category: Day
    timeRange: {
      startTime: "2020-07-01T00:00:00Z"
      endTime: "2020-07-10T00:00:00Z"
    }
    breakfastTime: {
      startTime: "07:00"
      endTime: "09:00"
    }
    lunchTime: {
      startTime: "12:00"
      endTime: "13:00"
    }
    dinnerTime: {
      startTime: "18:00"
      endTime: "19:00"
    }
  ) {
    totalCount
    nodes {
      date
      employee {
        uid
        name
      }
      breakfastCount
      lunchCount
      dinnerCount
    }
  }
}

由於是寫 RAW SQL,如果有使用 ORM 套件,要注意 SQL Injection 部分。歡迎大家提供更好的寫法或 DB 結構。


See also