【问题标题】:Design to represent employee check-in and check-out设计代表员工入住和退房
【发布时间】:2012-09-28 01:14:50
【问题描述】:

我目前有一个表格,代表员工的开始和停止工作时间:

  • id_employee int
  • 签到日期时间
  • 结帐日期时间

员工完成后需要更新 check_out。

最好有如下表格吗?

  • id_employee int
  • date_event 日期时间
  • event_type varchar,值可以是 CHECKIN 或 CHECKOUT。

要确定员工是否已经签到,我只需检查给定员工的最后一条记录的 event_type 是否为 CHECKIN。此外,不再需要获取记录并对其进行更新。

第二种方法更好吗?或者您有其他建议吗?

【问题讨论】:

  • 如果您选择第二个选项,请使用 ENUM('CHECKIN', 'CHECKOUT') 作为 event_type 列。它更小,更容易使用固定表格格式,比动态表格提供更好的性能。

标签: mysql database-design


【解决方案1】:

我知道这篇文章已经过时了,但这是给仍在寻找解决方案的人的:

考勤表结构

id              | int
employee_code   | varchar
status          | enum('check_in','check_out')
created         | datetime

数据

id  employee_code   status          created

1   EMP0001         check_in        2016-08-20 09:30:30
2   EMP0001         check_out       2016-08-20 18:15:00
3   EMP0002         check_in        2016-08-21 14:52:48
4   EMP0002         check_out       2016-08-21 21:09:18

查询

SELECT 
  A1.employee_code,
  A1.created AS check_in_at,
  A2.created AS check_out_at,
  TIMEDIFF(A2.created, A1.created) AS total_time 
FROM
  tbl_attendances AS A1 
  INNER JOIN tbl_attendances AS A2 
    ON A1.employee_code = A2.employee_code 
    AND DATE(A1.created) = DATE(A2.created) 
WHERE 1 = 1 
  AND A1.status = 'check_in' 
  AND A2.status = 'check_out' 
  AND DATE(A1.created) BETWEEN '2016-08-20' 
  AND '2016-08-21' 
  AND DATE(A2.created) BETWEEN '2016-08-20' 
  AND '2016-08-21' 
ORDER BY A1.created DESC

结果

employee_code   check_in_at            check_out_at           total_time

EMP0002         2016-08-21 14:52:48    2016-08-21 21:09:18    06:16:30
EMP0001         2016-08-20 09:30:30    2016-08-20 18:15:00    08:44:30

对于特定员工,在WHERE 子句中添加AND A1.employee_code = 'EMP0001'

【讨论】:

  • 对不起,这对我不起作用,我已经测试了你的解决方案,我得到了虚假信息,你能编辑你的代码吗??!!!!
  • @csharp_devloper31 你能详细说明一下吗?所以我知道您面临什么问题并相应地解决我的答案。
  • 只有当日有check_out记录时才会产生结果吧?
【解决方案2】:

像往常一样,“这取决于”。

选项 1 更易于构建,也更易于查询。找出谁签入但没有签出是一个简单的查询;查找每位员工的总工作时间也很简单。这种简单性可能意味着普通查询会更快。我看到的唯一缺点是它更难扩展。例如,如果您想为“午休”捕获不同的事件类型,则必须添加额外的列。

选项 2 更加灵活 - 您可以在不更改架构的情况下添加新的事件类型。然而,简单的查询——员工 x 在 6 月份工作了多少小时——是相当棘手的。您需要付出巨大的额外努力才能获得灵活性。

所以,这取决于您所说的“更好”是什么意思。

【讨论】:

  • “更好”,就像数据库设计的半神会做的那样。沿着三个 NF 的思路思考 :) 。考虑了灵活性。现在你提到它,计算时间会很混乱。另一个可能的缺点是,如果需要将值映射到对象。
  • 我很确定,数据库设计的“半神”会想出一个尽可能简单地满足当前和已知未来需求的解决方案;如果您不需要使用不同类型的事件扩展应用程序,请使用选项 1。它更易于使用,并且没有人为“优雅的数据库设计”颁发奖品。
  • 请记住,您可能(将来,如果尚未建立)需要一种方法让经理“修复”时间条目。例如,如果员工在上午 8 点上班,然后在下午 5 点下班,但确实休息了一段时间,但从未为此打卡上班/下班,那么您必须有办法让经理将休息时间从中午 12 点插入到下午 1 点。对于第一个设计,您必须将记录从下午 5 点更改为下午 12 点,然后输入从下午 1 点到下午 5 点的新记录。这就是为什么我更喜欢选项 2;您只需要插入 2 个拳头并让您的应用程序逻辑处理计算/排序。
【解决方案3】:

格式 #2 更好,因为:

  1. 此表只是一个打卡记录条目 - 即使它有异常也没关系。
  2. 以后此表将扩展,例如,您可能想再引入两个事件 INTERVAL_OUT、INTERVAL_IN。第二种格式会保持简单。
  3. 如果可能,请使用 event_type_id 而不是 event_type 和另一个表 event_type 或只是一个常量数组,例如。

    array_event_name = 数组 (1=>CHECKIN, 2=>CHECKOUT, 3=>INTERVAL_IN, 4=>INTERVAL_OUT)

【讨论】:

    【解决方案4】:

    我会选择第二个。

    但是,主要问题和业务规则将是相同的,并且可以通过任何一种方法来回答。

    【讨论】:

      【解决方案5】:

      选项 #1

      使用第一个选项,数据库本身可以更好地保护自己免受一些异常的影响1。仍有可能出现一些异常2,但这只是一个开始。

      另一方面,InnoDB 表是集群的,集群表中的二级索引可能很昂贵(请参阅this article 中的“集群的缺点”),如果您需要,请考虑这一点在check_out上查询。

      选项 #2

      使用第二个选项,即使是可以通过数据库设计纯粹以声明方式防止的异常,您也依赖命令式代码。

      从好的方面来说,您不太可能需要二级索引。

      选择

      所以简而言之,选择第一个选项,除非您需要二级索引。如果您确实需要二级索引,具体取决于您希望实现的index covering 类型,您可以选择任一选项。


      1比如不先签到就签出。

      2 比如再次签到,没有第一次签出,重叠“限制”等等……

      【讨论】:

        【解决方案6】:

        我会选择这里的第一个选项。将两个时间戳放在一行中会增加您的搜索时间,并使您的计算更容易。

        假设您要计算员工一天的工作时间。您的搜索将在匹配的第一行停止,您将获得所有必需的数据。您不必深入挖掘,选项 2 并非如此。选项 1 还通过每次签入/签出仅使用 1 行来减少您的表格大小。

        不过,选项 2 确实有一个优势。签出时,您的数据库必须进行搜索以更新选项 1 的数据。对于选项 2,它只是一个写入。

        考虑到您将多次搜索数据这一事实,您可以放弃直接插入的优势以获得更好的结构和更快的搜索速度。虽然最终的选择取决于你。

        祝你好运!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-12-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-09-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多