1

ねえ、mySQL テーブルでのイベントのオーバーラップの解決策を探しているこのサイトに出くわしました。私は解決策にとても感銘を受けました(すでに役に立っています)。さらに助けが得られるかどうか見てみようと思いました...

さて、ジョーは職場の誰かとシフトを交換したいと思っています。彼には法廷の日付があります。彼はシフト スワップ フォームにアクセスし、今週のスケジュール (または残りのスケジュール) を表示します。これは、DB クエリで実行されます。汗かいていない。彼はシフトを選びます。ここから、チクチクします。

したがって、最初に、フォームはシフト開始とシフト終了をスクリプトに渡します。このシフトと重複するシフトを持っている人に対してクエリを実行します。一度に 2 つのシフトで働くことはできないため、このクエリのすべてのユーザー 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 がこれをより簡単な方法で処理する天才的な方法を持っているか、誰かが潜在的な競合をよりスマートな方法で説明する素晴らしい論理プリンシパルを指摘できることを願っています。(「開始 > 終了、終了 < 開始」の使用に注意してください。その間に使用していたことがわかり、両端から 1 分を差し引く必要がありました。)

ありがとう!

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

値をTEMPORARYテーブルにロードjoe_shift{1,2,3} し、クエリを実行してそれに対して結合し、外部結合を使用して、どのシフトにも一致しないシフトのみを見つけることができます。

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 に答える