我有两张桌子,如下:
master
---------
empcode INT PRIMARY KEY
name VARCHAR
dept VARCHAR
emp_tx
----------
empcode INT references MASTER(empcode)
s_date DATETIME
emp_tx
表记录员工“in”和“out”事务。列s_date
存储“in”或“out”事件发生时的时间(作为DATETIME值)。交易记录从办公区域(通过指纹生物识别系统。)
来自emp_tX表的示例数据:
empcode s_datetime
------- ------------------
1110 2012-12-12 09:31:42 (employee in time to the office)
1110 2012-12-12 13:34:17 (employee out time for lunch)
1110 2012-12-12 14:00:17 (employee in time after lunch)
1110 2012-12-12 18:00:12 (employee out time after working hours)
1112
etc.
注意:
如果某个员工在某一天没有离开办公室,那么该日期的emp_tx
交易表中不会插入任何行。在给定日期缺少员工将由该员工的“缺失”行和该日期表示。
任何人都可以帮助我获取一个返回员工缺席日期的SQL查询,以生成员工缺席报告吗?
查询的输入将是两个DATE值,一个“from”日期和一个“to”日期,它指定一个日期范围。查询应该返回所有出现的“缺席”(或者,非事件,而不是,当EMP_TX
表中的empcode
表中没有在“来自”和“之间的任何日期的任何日期到“约会。
预期输出:
如果我们输入'2012-12-12'作为“from”日期,并将'2012-12-20'作为“to”日期输入,则查询应返回如下行:
Empcode EmpName Department AbsentDate TotalNoofAbsent days
------- ------- ---------- ----------- --------------------
1110 ABC Accounts 2012-12-12
1110 ABC Accounts 2012-12-14 2
1112 xyz Software 2012-12-19
1112 xyz Software 2012-12-17 2
我已经尝试过这个查询,我确信它没有返回我想要的行:
select tx.date from Emp_TX as tx where Date(S_Date) not between '2012-12-23' and '2012-12-30'
感谢。
答案 0 :(得分:2)
如果“缺席”被定义为特定日期(日期=午夜至午夜24小时)的特定emp_tx
的{{1}}表中的行不出现,并且...
如果对于该日期empcode
表中没有任何交易的日期(即排除该日期不存在所有代码的日期),可以接受不显示“缺席”,那么......
您可以使用以下查询获取指定结果集的前四列:(未经测试)
emp_tx
获得在同一结果集中返回的第五列SELECT m.empcode AS `EmpCode`
, m.name AS `EmpName`
, m.dept AS `Department`
, d.dt AS `AbsentDate`
FROM ( SELECT DATE(t.s_date) AS dt
FROM emp_tx t
WHERE t.s_date >= '2012-12-12'
AND t.s_date < DATE_ADD( '2012-12-20' ,INTERVAL 1 DAY)
GROUP BY DATE(t.s_date)
ORDER BY DATE(t.s_date)
) d
CROSS
JOIN master m
LEFT
JOIN emp_tx p
ON p.s_date >= d.dt
AND p.s_date < d.dt + INTERVAL 1 DAY
AND p.empcode = m.empcode
WHERE p.empcode IS NULL
ORDER
BY m.empcode
, d.dt
是可能的,但它会使该查询非常混乱。在处理返回的结果集时,可以在客户端更有效地处理此详细信息。
查询的工作原理
作为TotalNoofAbsent
别名的内联视图为我们提供了一组我们正在检查的“日期”值。使用d
表作为这些“日期”值的来源是一种方便的方法。 emp_tx
函数不仅仅返回DATETIME参数的“date”部分;我们使用DATE()
来获取不同的日期列表(即没有重复的值)。 (我们所使用的内联视图查询是在作为参数传递的两个值之间的一组不同的DATE值。还有其他更复杂的方法来生成DATE值列表。)
只要您将视为“缺席”的每个“日期”值出现在表格的某个位置(即,至少有一个GROUP BY
在每个感兴趣的日期都有一个交易),并且只要empcode
表中的行数不多,那么内联视图查询就能很好地工作。
(注意:内联视图中的查询可以单独运行,以验证结果是否正确并且正如我们所期望的那样。)
下一步是从内联视图中获取结果并执行emp_tx
操作(生成笛卡尔积)以将每个CROSS JOIN
与从{4}返回的每个empcode
匹配内联视图。此操作的结果表示每次可能出现的“出勤”。
查询的最后一步是使用date
和LEFT JOIN
谓词执行“反加入”操作。 WHERE IS NULL
(外部联接)返回每个可能的出席事件(从左侧),包括那些没有来自LEFT JOIN
表的匹配行(出勤记录)。
“技巧”是包含一个谓词(在WHERE子句中),该谓词丢弃找到匹配的考勤记录的所有行,因此我们剩下的就是emp_tx
和{的所有组合{1}}(可能的出勤事件),没有匹配的出勤交易。
(注意:我故意在谓词中将对s_date(DATETIME)列的引用保留为“bare”,并使用范围谓词。这将允许MySQL有效地使用包含该列的适当索引。)
如果我们将列引用包装在函数内的谓词中,例如empcode
,那么MySQL将无法有效利用date
列上的索引。
正如其中一条评论(关于你的问题)所指出的那样,我们没有对将员工标记为“进入”或“外出”的交易进行任何区分。我们只是在给定的24小时“午夜到午夜”期间寻找该代码的交易。
还有其他方法可以获得相同的结果集,但“反连接”模式通常可以提供大集合的最佳性能。
为了获得最佳性能,您可能需要覆盖索引:
DATE(p.s_date)
答案 1 :(得分:0)
不幸的是,您的查询将为您带来大量结果......它将始终返回您所提供范围之外的员工的所有日期。您想检查NOT EXISTS您的日期之间的记录。
可以在纯SQL中执行此操作...如果不使用游标或特定于DB的某些内容,我无法想到一种方法。这个Java伪代码将为您提供1名员工的缺席:
List<Date> findAbsences(int empCode, Date inDate, Date outDate) {
List<Date> result = new LinkedList<Date>();
Calendar c = new Calendar();
c.setTime(new Date(2012,12,12));
while (!c.getTime().after(outDate)) {
// run query for EMP_TX records between inDate & outDate
//SELECT 1 FROM EMP_TX WHERE EmpCode = :empid AND S_Date BETWEEN :in AND :out;
if (!query.hasNext()) {
result.add(c.getTime);
}
c.add(Calendar.DATE, 1);
}
}