6

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

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

当员工完成时,它需要更新 check_out。

最好有一张如下表吗?

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

要确定员工是否已经签入,我必须检查给定员工的最后一条记录是否具有 CHECKIN 的 event_type。此外,不再需要获取记录并更新它。

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

4

6 回答 6

8

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

考勤表结构

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

AND A1.employee_code = 'EMP0001'对于特定员工添加WHERE条款

于 2017-11-01T14:33:27.787 回答
5

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

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

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

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

于 2012-09-28T11:27:13.640 回答
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)

于 2012-09-28T09:28:49.997 回答
1

我会选择第二个。

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

于 2012-09-28T01:18:06.310 回答
1

选项1

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

另一方面,InnoDB 表是集群的,集群表中的二级索引可能很昂贵(请参阅本文中的“集群的缺点” ,如果您需要在check_out.

选项 #2

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

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

选择

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


1如不先签入就签出。

2如再次入住、未先退房、重叠“进站”等……

于 2012-09-28T03:23:12.563 回答
1

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

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

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

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

祝你好运!

于 2012-09-28T13:35:59.503 回答