-2

我有两张桌子,像这样:

master
---------
empcode    INT PRIMARY KEY
name       VARCHAR
dept       VARCHAR 

emp_tx
----------
empcode    INT        references MASTER(empcode)
s_date     DATETIME

emp_tx表记录了员工“进”和“出”的交易。该列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 值,一个“开始”日期和一个“结束”日期,它指定日期范围。查询应该返回所有出现的“缺席”(或者,当在“从”和“到”日期之间的任何日期在EMP_TX表中没有找到任何行时,不出现而不是,非出现。empcode

预期输出:

如果我们输入“2012-12-12”作为“开始”日期,“2012-12-20”作为“结束”日期,查询应该返回如下行:

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'

谢谢。

4

2 回答 2

2

如果“缺席”被定义为在特定日期(日期=午夜到午夜 24 小时期间)的emp_tx特定表中没有出现一行,并且...empcode

如果在该日期的表中没有交易的日期不显示“缺席”是可以接受的emp_tx(即排除该日期所有 empcode 都缺席的日期),那么...

您可以使用如下查询获取指定结果集的前四列:(未经测试)

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是可能的,但它会使查询变得非常混乱。在处理返回的结果集时,这个细节可能会在客户端更有效地处理。


查询的工作原理

别名为 as 的内联视图为d我们提供了一组我们正在检查的“日期”值。使用emp_tx表格作为这些“日期”值的来源是一种方便的方法。不是DATE()函数只返回 DATETIME 参数的“日期”部分;我们使用 aGROUP BY来获取不同的日期列表(即没有重复值)。(通过这个内联视图查询,我们所追求的是作为参数传入的两个值之间的一组不同的 DATE 值。还有其他更复杂的生成 DATE 值列表的方法。)

只要您将视为“缺席”的每个“日期”值都出现在表中的某处(即,empcode每个感兴趣的日期至少有一个交易),并且只要该emp_tx表并不过分,则内联视图查询将运行得相当好。

(注意:内联视图中的查询可以单独运行,以验证结果是否正确且符合我们的预期。)

下一步是从内联视图中获取结果并执行CROSS JOIN操作(以生成笛卡尔积)以匹配 EVERYempcodedate从内联视图返回的 EVERY。这个操作的结果代表了每一个可能出现的“出席”。

LEFT JOIN查询的最后一步是使用 a和WHERE IS NULL谓词执行“反连接”操作。(LEFT JOIN外连接)返回每个可能的出勤事件(从左侧),包括那些没有从emp_tx表中匹配的行(出勤记录)。

“技巧”是包含一个谓词(在 WHERE 子句中),该谓词丢弃找到匹配出勤记录的所有行,这样我们剩下的就是所有empcodedate(可能的出勤事件)的组合,其中没有匹配出勤事务。

(注意:我故意在谓词中保留对 s_date (DATETIME) 列的引用,并使用范围谓词。这将允许 MySQL 有效地使用包含该列的适当索引。)

如果我们将列引用包装在函数 eg 的谓词中DATE(p.s_date),那么 MySQL 将无法有效地使用s_date列上的索引。


正如其中一条评论(关于您的问题)指出的那样,我们不会对将员工标记为“进来”或“出去”的交易进行任何区分。我们只是在给定的 24 小时“午夜到午夜”期间寻找该 empcode 的交易的存在。


还有其他方法可以获得相同的结果集,但“反连接”模式通常会在大集合中提供最佳性能。

为了获得最佳性能,您可能需要覆盖索引:

... ON master (empcode, name, dept)

... ON emp_tx (s_date, empcode)
于 2012-12-24T18:12:10.907 回答
0

不幸的是,您的查询会给您带来大量结果......它总是会返回您提供的范围之外的员工的所有日期。您想检查日期之间是否存在记录。

可以在纯 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);
    }


}
于 2012-12-24T17:03:03.093 回答