设计代表员工登记入住和退房

时间:2012-09-28 01:14:51

标签: mysql database-design

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

  • id_employee int
  • check_in datetime
  • check_out datetime

员工完成后需要更新check_out。

最好有一张表如下吗?

  • id_employee int
  • date_event datetime
  • event_type varchar,值可以是CHECKIN或CHECKOUT。

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

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

6 个答案:

答案 0 :(得分:3)

格式#2更好,因为:

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

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

答案 1 :(得分:3)

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

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

选项2更灵活 - 您可以在不更改架构的情况下添加新事件类型。但是,简单的查询 - 员工x在6月工作了多少小时 - 非常棘手。您需要付出额外努力的灵活性。

所以,这取决于你所说的“更好”。

答案 2 :(得分:3)

我知道这篇文章已经过时了,但这适用于那些仍在寻找解决方案的人:

出勤表格结构

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

答案 3 :(得分:1)

我会选择第二个。

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

答案 4 :(得分:1)

选项#1

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

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

选项#2

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

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

选择

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


1 例如在没有首次办理登机手续的情况下退房。

2 例如再次办理登机手续,无需先退房,重叠“stints”等...

答案 5 :(得分:1)

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

假设您想计算一天员工的工作时间。您的搜索将在匹配的第一行停止,您将获得所有必需的数据。您不必深入挖掘选项2的情况。选项1还通过每次登记/退房仅使用1行来减少您的工作台大小。

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

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

祝你好运!