1

嘿,我偶然发现这个网站正在寻找 mySQL 表中事件重叠的解决方案。这个解决方案给我留下了深刻的印象(这已经很有帮助)我想我会看看我是否能得到更多帮助......

好的,所以乔想和工作的人换班。他有一个出庭日期。他去了换班表格,它拉起了本周的时间表(或剩下的时间表)。这是通过数据库查询完成的。没有汗水。他选择换班。从这一点开始,它变得多刺。

因此,首先,表单将 shift start 和 shift end 传递给脚本。它会针对与此班次重叠的班次的任何人运行查询。他们不能同时工作两班,因此该查询中的所有用户 ID 都被列入黑名单。此查询如下所示:

SELECT DISTINCT user_id FROM shifts
WHERE
FROM_UNIXTIME('$swap_shift_start') < shiftend
AND FROM_UNIXTIME('$swap_shift_end') > shiftstart

接下来,我们对所有班次运行查询,这些班次 a) 长度相同(公司政策),并且 b) 不与 Joe 正在工作的任何其他班次重叠。

我目前拥有的是这样的:

SELECT *
FROM shifts
AND shiftstart BETWEEN  FROM_UNIXTIME('$startday') AND FROM_UNIXTIME('$endday')
AND user_id NOT IN ($busy_users) 
AND (TIME_TO_SEC(TIMEDIFF(shiftend,shiftstart)) = '$swap_shift_length')
$conflict_dates
ORDER BY shiftstart, lastname

现在,您可能想知道“$conflict_dates 是什么???”

好吧,当乔提交交换班次时,它会重新加载他一周的班次,以防他决定检查另一个班次的潜力。所以当它执行第一个查询时,当脚本循环并输出他的选择时,它也在构建一个看起来像这样的字符串:

AND NOT(
'joe_shift1_start' < shiftend
AND 'joe_shift1_end' > shiftstart)
AND NOT(
'joe_shift2_start' < shiftend
AND 'joe_shift2_end' > shiftstart)
...etc

这样数据库就会得到一个相当长的查询,如下所示:

SELECT *
FROM shifts
AND shiftstart BETWEEN  FROM_UNIXTIME('$startday') AND FROM_UNIXTIME('$endday')
AND user_id NOT IN ('blacklisteduser1', 'blacklisteduser2',...etc) 
AND (TIME_TO_SEC(TIMEDIFF(shiftend,shiftstart)) = '$swap_shift_length')
AND NOT(
'joe_shift1_start' < shiftend
AND 'joe_shift1_end' > shiftstart)
AND NOT(
'joe_shift2_start' < shiftend
AND 'joe_shift2_end' > shiftstart)
AND NOT(
'joe_shift3_start' < shiftend
AND 'joe_shift3_end' > shiftstart)
AND NOT(
'joe_shift4_start' < shiftend
AND 'joe_shift4_end' > shiftstart)
...etc
ORDER BY shiftstart, lastname

所以,我的希望是,要么 SQL 有一些天才的方式以更简单的方式处理这个问题,要么有人可以指出一个奇妙的逻辑原理,以更智能的方式解释潜在的冲突。(请注意使用“开始>结束,结束<开始”,然后我发现我正在使用中间并且不得不从两端减去一分钟。)

谢谢!

一种

4

2 回答 2

3

我认为您应该能够使用内部选择而不是生成的字符串来排除乔的其他班次,例如:

SELECT *
FROM shifts s1
AND shiftstart BETWEEN  FROM_UNIXTIME('$startday') AND FROM_UNIXTIME('$endday')
AND user_id NOT IN ($busy_users) 
AND (TIME_TO_SEC(TIMEDIFF(shiftend,shiftstart)) = '$swap_shift_length')
AND (SELECT COUNT(1) FROM shifts s2
     WHERE s2.user_id = $joes_user_id
     AND   s1.shiftstart < s2.shiftend
     AND   s2.shiftstart < s1.shiftend) = 0
ORDER BY shiftstart, lastname

基本上,每一行都有一个内部查询,用于查询 Joe 重叠的班次计数,并确保它为零。因此,只会返回不与 Joe 的任何现有班次重叠的行。

于 2008-12-28T15:01:15.723 回答
1

您可以将joe_shift{1,2,3} 值加载到 TEMPORARY 表中,然后执行查询以对其进行联接,使用外部联接仅查找不匹配的班次:

CREATE TEMPORARY TABLE joes_shifts (
 shiftstart DATETIME
 shiftend   DATETIME
);
INSERT INTO joes_shifts (shiftstart, shiftend) VALUES
  ('$joe_shift1_start', '$joe_shift1_end'),
  ('$joe_shift2_start', '$joe_shift2_end'),
  ('$joe_shift3_start', '$joe_shift3_end'),
  ('$joe_shift4_start', '$joe_shift4_end');
-- make sure you have validated these variables to prevent SQL injection

SELECT s.*
FROM shifts s
  LEFT OUTER JOIN joes_shifts j
  ON (j.shiftstart < s.shiftend OR j.shiftend > s.shiftstart) 
WHERE j.shiftstart IS NULL
  AND s.shiftstart BETWEEN FROM_UNIXTIME('$startday') AND FROM_UNIXTIME('$endday')
  AND s.user_id NOT IN ('blacklisteduser1', 'blacklisteduser2',...etc) 
  AND (TIME_TO_SEC(TIMEDIFF(s.shiftend,s.shiftstart)) = '$swap_shift_length');

由于 LEFT OUTER JOIN,当 中没有匹配joes_shifts的行时,列为 NULL。

于 2008-12-28T08:45:33.407 回答