1

我有一张桌子hirefire。下面是它的简化结构:

  • hired 日期
  • fired 日期
  • firereason小字

我花了几个小时为这个表编写查询来解决一个问题,我的问题(简化)如下所示:

此表描述了员工何时开始工作以及何时去度假。

hired..fired此表中一行的间隔(hired包括和fired不包括在间隔中)称为“雇用间隔”。我保证租用间隔不会相互覆盖,也不会覆盖fired>=hired每一行。

我将所有间隔称为“触发间隔” fired1..hired2,其中fired1是该表中fired一行的字段,r1并且hired2是该表hired下一行r2中的字段,其中行按字段排序hired。对于每个这样的时间间隔,它都被分配了一个“解雇原因”(对包含离职原因的表的主 ID 的引用,例如假期、育儿假、死亡等),等于字段firereasonfrom r1

假设给定一个月(通过包含该月第一天的 SQL DATE 变量)。

我需要给定月份的火灾间隔的非空交集。(也就是说,我需要有关这些间隔的信息,这些间隔与给定的月份至少有一天相同。)

MySQL+PHP

4

1 回答 1

2

所以你必须找到所有的 [fired,hired] 间隔。

我这里的方法是首先选择给定范围内的所有“触发日期”(在所需间隔的开始和结束时合成假触发日期)。然后对雇佣日期做同样的事情——最后成对匹配。

作为一张价值 1000 字的图片,这里以图形方式展示了查询的工作原理: 查找不在范围内的值的查询

这导致了一个相当复杂的查询(而且效率低下——可能需要几个临时表 + 文件排序):

SELECT * FROM
( -- Keep numbered list of "fired" date
SELECT (@i := @i+1) as n, F.* FROM (
  SELECT @start AS fired 
  UNION SELECT fired FROM hirefire
    WHERE fired > @start and hired < @end
  UNION SELECT @end
  ) AS F
   JOIN (SELECT @i := 0) AS init -- initialize @i
   ORDER BY F.fired ASC
) AS F
JOIN
( -- Keep numbered list of "hired" date
SELECT (@j := @j+1) as n, H.* FROM (
  SELECT @start AS hired 
  UNION SELECT hired FROM hirefire
    WHERE fired > @start and hired < @end
  UNION SELECT @end
  ) AS H
   JOIN (SELECT @j := 0) AS init -- initialize @j
   ORDER BY H.hired ASC
) AS H
ON( F.n+1 = H.n )
WHERE H.hired <> F.fired;

有关实时示例,请参阅http://sqlfiddle.com/#!2/a841d0/39

举个例子:

create table hirefire(pk serial, hired int, fired int);
insert into hirefire(hired, fired) values
 (1,3), (5,10), (12,14), (16,25);
SET @start = 4;
SET @end = 30;

会产生

+----+--------+-------+
| N  | FIRED  | HIRED |
+----+--------+-------+
| 1  |     4  |     5 |
| 2  |    10  |    12 |
| 3  |    14  |    16 |
| 4  |    25  |    30 |
+----+--------+-------+

再解释几句:

  • 如您所见,我使用用户定义的变量来编号行(需要轻松地成对匹配)
  • 我使用这个JOIN (SELECT @j := 0)技巧来初始化这些变量,而不需要单独的SET ...语句
  • 我在这里通过使用整数范围来简化问题,以便通过减少“噪音”来保持答案易于理解。你将不得不适应它DATETIME
  • 我使用“纯 SQL”来找到最初需要的答案,但由于存在一些基于“行号”的匹配,因此在应用程序级别解决这部分问题可能是最有效的;)

这里仅供参考我的原始答案。它通过发出 [hired,fired] 间隔来生成正确查询的补充

假设您有一个间隔 [@start @end)

SELECT DISTINCT GREATEST(@start, hired), LEAST(@end, fired)
 FROM hirefire
 WHERE @start < fired AND @end >= hired;

我不太确定不平等/严格不平等的事情,但这就是精神。

有关示例,请参见http://sqlfiddle.com/#!2/a841d0/7 。DATETIME它使用纯整数来定义范围,但我认为您无需付出太多努力就可以适应它。

于 2013-08-11T18:08:25.623 回答