好的,所以Joe希望与工作中的某个人交换班次。他有一个法庭约会。他进入班次交换表格,它拉起了本周的时间表(或剩下的时间表)。这是通过数据库查询完成的。没有汗水。他选择了一个班次。从这一点来说,它变得棘手。
因此,首先,表单将shift开始和shift结束传递给脚本。它为任何具有与此班次重叠的班次的人运行查询。它们不能同时处理两个班次,因此此查询中的所有用户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能够以一种更简单的方式处理这个问题,或者有人可以指出一个奇妙的逻辑主体,以更智能的方式解释潜在的冲突。 (注意使用'start&gt; end,end&lt; start',然后才发现我正在使用它们之间并且必须从两端减去一分钟。)
谢谢!
A
答案 0 :(得分:3)
我认为你应该能够使用内部选择而不是生成的字符串来排除Joe的其他移位,例如:
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的任何现有班次不重叠的行。
答案 1 :(得分:1)
您可以将joe_shift{1,2,3}
值加载到TEMPORARY表中,然后执行查询以加入它,使用外部联接仅查找与任何不匹配的shift:
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。