SQL:一个月内的子间隔
Posted
技术标签:
【中文标题】SQL:一个月内的子间隔【英文标题】:SQL: Sub-intervals which are inside a month 【发布时间】:2013-08-11 17:53:07 【问题描述】:我有一张桌子hirefire
。下面是它的简化结构:
hired
日期
fired
日期
firereason
smallint
我花了几个小时为这个表编写查询来解决一个问题,我的问题(简化)如下所示:
此表描述了员工何时开始工作以及何时去度假。
此表的一行中的间隔hired..fired
(包括hired
,不包括fired
)称为“雇用间隔”。我保证租用间隔不会相互覆盖,并且每一行都不会覆盖fired>=hired
。
我将所有间隔称为“触发间隔”fired1..hired2
,其中fired1
是此表中r1
行中的字段fired
,hired2
是下一行r2
中的字段hired
此表的行按字段hired
排序。对于每个这样的时间间隔,它都被分配了一个“火灾原因”(对包含离职原因的表的主要 ID 的引用,例如假期、育儿假、死亡等),等于字段 firereason
来自r1
.
假设给定一个月(通过包含该月第一天的 SQL DATE 变量)。
我需要给定月份的火灾间隔的非空交集。 (也就是说,我需要有关这些间隔的信息,这些间隔与给定的月份至少有一天相同。)
【问题讨论】:
"解雇日期" = "他去度假的时候" :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:一个月内的子间隔的主要内容,如果未能解决你的问题,请参考以下文章