SQL:一个月内的子间隔

Posted

技术标签:

【中文标题】SQL:一个月内的子间隔【英文标题】:SQL: Sub-intervals which are inside a month 【发布时间】:2013-08-11 17:53:07 【问题描述】:

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

hired日期 fired日期 firereasonsmallint

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

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

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

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

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

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

mysql+php

【问题讨论】:

"解雇日期" = "他去度假的时候" :D 您的描述可能缺少某种标识符以了解 被雇用/解雇? @SylvainLeroux:是的,它缺少员工 ID。为简单起见,我省略了它 我决定放弃在 SQL 中解决这个问题的尝试。我将从数据库中读取 所有 数据(对于给定的员工)并在 PHP 中处理它,而无需进一步的 SQL 查询 【参考方案1】:

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

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

作为一张价值 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

【讨论】:

这不是我需要的。我需要间隔 @porton 我可能不明白......但就我自己而言,LEAST(...)GREATEST(...) 之间的范围 一个区间。实际上,查询返回每个区间的所有非空交集与 [@start, @end)。由于这显然更复杂,您能否提供自己的 sqlfiddle 作为(简化)示例 + expected 输出。 我需要间隔来自表的两个 不同(相邻)行的值。这是复杂的部分。查看问题 Sylvain:问题是检索该人不工作的所有时间间隔(火灾时间间隔)。您的查询检索该人工作的所有时间间隔(雇用时间间隔)。 @SteveKass 谢谢史蒂夫:这正是我遗漏的关键点。同样的方式,让问题更有趣!

以上是关于SQL:一个月内的子间隔的主要内容,如果未能解决你的问题,请参考以下文章

sql怎么生成某一个时段内的随机时间戳

时间间隔内的 SQL 计数

NSCalendar 一个月内的最大天数

typescript 一个月内的日子

从 MySQL 日期获取一个月内的用户数

mysql中怎么查询一周内,三个月内,半年内的数据?