このソリューションを使用して、条件 (1 ~ 5 としましょう) 内のすべてuserids
のユーザーが満たすことができる「最適な」時間枠を見つけることができます。「最適な」時間枠は、最大の秒数で測定されます。
SELECT MAX(b.FromDateTime) FromDateTime,
a.ToDateTime
FROM (
SELECT DISTINCT a.ToDateTime
FROM tbl a
JOIN tbl b ON a.userid <> b.userid
AND a.userid IN (1,2,3,4,5)
AND b.userid IN (1,2,3,4,5)
AND a.ToDateTime > b.FromDateTime
AND a.ToDateTime <= b.ToDateTime
GROUP BY a.userid,
a.FromDateTime,
a.ToDateTime
HAVING COUNT(DISTINCT b.userid) = 4
) a
JOIN (
SELECT DISTINCT a.FromDateTime
FROM tbl a
JOIN tbl b ON a.userid <> b.userid
AND a.userid IN (1,2,3,4,5)
AND b.userid IN (1,2,3,4,5)
AND a.FromDateTime >= b.FromDateTime
AND a.FromDateTime < b.ToDateTime
GROUP BY a.userid,
a.FromDateTime,
a.ToDateTime
HAVING COUNT(DISTINCT b.userid) = 4
) b ON b.FromDateTime < a.ToDateTime
GROUP BY a.ToDateTime
ORDER BY TIMESTAMPDIFF(SECOND, MAX(b.FromDateTime), a.ToDateTime) DESC
LIMIT 1
4
afterは、COUNT(DISTINCT...
基準内のユーザー数から 1 を引いたものです (ユーザーは自分自身に参加できないため)。それに応じて調整します。
返されるのは、すべてのユーザーが参加できる会議の開始時間と終了時間です。
クエリの内訳
次のデータがあるとします。
(62, 1, '2012-07-18 00:00:00', '2012-07-18 12:00:00', 1),
(63, 2, '2012-07-18 00:00:00', '2012-07-18 02:00:00', 1),
(64, 2, '2012-07-18 03:00:00', '2012-07-18 05:00:00', 1),
(65, 2, '2012-07-18 05:30:00', '2012-07-18 06:00:00', 1),
(66, 3, '2012-07-18 00:30:00', '2012-07-18 02:30:00', 1),
(67, 3, '2012-07-18 03:10:00', '2012-07-18 07:30:00', 1),
(68, 4, '2012-07-18 01:10:00', '2012-07-18 03:20:00', 1),
(69, 4, '2012-07-18 03:50:00', '2012-07-18 06:00:00', 1),
(70, 5, '2012-07-18 01:10:00', '2012-07-18 03:20:00', 1),
(71, 5, '2012-07-18 04:30:00', '2012-07-18 07:10:00', 1),
(72, 1, '2012-07-18 13:00:00', '2012-07-18 14:00:00', 1),
(73, 2, '2012-07-18 13:30:00', '2012-07-18 14:30:00', 1),
(74, 3, '2012-07-18 14:00:00', '2012-07-18 15:00:00', 1),
(75, 4, '2012-07-18 14:30:00', '2012-07-18 15:30:00', 1),
(76, 5, '2012-07-18 18:00:00', '2012-07-18 19:00:00', 1);
相対的な時間間隔の位置は、次のテキストの図のようになります (すべてを表示するには、横スクロールする必要があります)。
uid 1 <--------------------------------------------------------------------------------------...--------> <-------------------->
uid 2 <-----------------------> <-----------------------> <----> <-------------------->
uid 3 <-----------------------> <-------------------------------------------> <-------------------->
uid 4 <-----------------------> <-----------------------> <-------------------->
uid 5 <-----------------------> <-----------------------> <-------------------->
[ 1 ] [2] [ 3 ] [ 4 ]
^
We want the start and end times of this overlap
括弧内の数字は、[
]
すべてのユーザーの自由時間が重複する時間枠を表します。オーバーラップ #1 が最も長いので必要です。オーバーラップ #1 は2012-07-18 1:10:00
to2012-07-18 2:00:00
である必要があるため、期待される結果は次のようになります。
FromDateTime | ToDateTime
----------------------------------------
2012-07-18 1:10:00 | 2012-07-18 2:00:00
ステップ1:
最初に行う必要があるのは、すべての潜在的な会議ウィンドウの終了時刻を把握することです。これは、終了時間が他のすべてのユーザーの自由時間間隔の間にある特定の間隔を選択することによって行われます。
返される終了時間は、上のテキストの図で指摘されている各オーバーラップの終了時間を表します。同じ終了時刻が 2 つ返された場合は、その特定の会議が終了するまでの最終時刻であるという事実以外に、その終了時刻について他に何も知る必要がないため、1 つだけを選択します。
SELECT DISTINCT a.ToDateTime
FROM tbl a
JOIN tbl b ON a.userid <> b.userid
AND a.userid IN (1,2,3,4,5)
AND b.userid IN (1,2,3,4,5)
AND a.ToDateTime > b.FromDateTime
AND a.ToDateTime <= b.ToDateTime
GROUP BY a.userid,
a.FromDateTime,
a.ToDateTime
HAVING COUNT(DISTINCT b.userid) = 4
レンダリング:
TODATETIME
-------------------
2012-07-18 02:00:00
2012-07-18 05:00:00
2012-07-18 06:00:00
2012-07-18 03:20:00
SQLFiddle デモ
ステップ2:
次に行う必要があるのは、前の手順と逆の手順を実行して、可能性のある各会議ウィンドウの開始時刻をすべて把握し、このクエリの結果を前の手順の結果と結合することです。開始時間が前のステップの終了時間よりも短い:
SELECT b.FromDateTime,
a.ToDateTime
FROM (
SELECT DISTINCT a.ToDateTime
FROM tbl a
JOIN tbl b ON a.userid <> b.userid
AND a.userid IN (1,2,3,4,5)
AND b.userid IN (1,2,3,4,5)
AND a.ToDateTime > b.FromDateTime
AND a.ToDateTime <= b.ToDateTime
GROUP BY a.userid,
a.FromDateTime,
a.ToDateTime
HAVING COUNT(DISTINCT b.userid) = 4
) a
JOIN (
SELECT DISTINCT a.FromDateTime
FROM tbl a
JOIN tbl b ON a.userid <> b.userid
AND a.userid IN (1,2,3,4,5)
AND b.userid IN (1,2,3,4,5)
AND a.FromDateTime >= b.FromDateTime
AND a.FromDateTime < b.ToDateTime
GROUP BY a.userid,
a.FromDateTime,
a.ToDateTime
HAVING COUNT(DISTINCT b.userid) = 4
) b ON b.FromDateTime < a.ToDateTime
ORDER BY a.ToDateTime, b.FromDateTime --Ordered for display purposes
レンダリング:
TODATETIME | FROMDATETIME
------------------------------------------
2012-07-18 02:00:00 | 2012-07-18 01:10:00 <-- Most recent FromDateTime
2012-07-18 03:20:00 | 2012-07-18 01:10:00
2012-07-18 03:20:00 | 2012-07-18 03:10:00 <-- Most recent FromDateTime
2012-07-18 05:00:00 | 2012-07-18 01:10:00
2012-07-18 05:00:00 | 2012-07-18 03:10:00
2012-07-18 05:00:00 | 2012-07-18 04:30:00 <-- Most recent FromDateTime
2012-07-18 06:00:00 | 2012-07-18 01:10:00
2012-07-18 06:00:00 | 2012-07-18 03:10:00
2012-07-18 06:00:00 | 2012-07-18 04:30:00
2012-07-18 06:00:00 | 2012-07-18 05:30:00 <-- Most recent FromDateTime
最新のFromDateTimes
ものは、潜在的な各会議ウィンドウの開始を表します。FromDateTime
ごとに最新の行のみをプルしたいToDateTime
。これは、次のステップで集計関数GROUP BY
と組み合わせて使用します。MAX()
SQLFiddle デモ
ステップ 3:
次に、GROUP BY
onToDateTime
とMAX()
onを使用してFromDateTime
、最新のものだけをプルしますFromDateTimes
。
SELECT MAX(b.FromDateTime) FromDateTime,
a.ToDateTime
FROM (
SELECT DISTINCT a.ToDateTime
FROM tbl a
JOIN tbl b ON a.userid <> b.userid
AND a.userid IN (1,2,3,4,5)
AND b.userid IN (1,2,3,4,5)
AND a.ToDateTime > b.FromDateTime
AND a.ToDateTime <= b.ToDateTime
GROUP BY a.userid,
a.FromDateTime,
a.ToDateTime
HAVING COUNT(DISTINCT b.userid) = 4
) a
JOIN (
SELECT DISTINCT a.FromDateTime
FROM tbl a
JOIN tbl b ON a.userid <> b.userid
AND a.userid IN (1,2,3,4,5)
AND b.userid IN (1,2,3,4,5)
AND a.FromDateTime >= b.FromDateTime
AND a.FromDateTime < b.ToDateTime
GROUP BY a.userid,
a.FromDateTime,
a.ToDateTime
HAVING COUNT(DISTINCT b.userid) = 4
) b ON b.FromDateTime < a.ToDateTime
GROUP BY a.ToDateTime
レンダリング:
FROMDATETIME | TODATETIME
-----------------------------------------
2012-07-18 01:10:00 | 2012-07-18 02:00:00
2012-07-18 03:10:00 | 2012-07-18 03:20:00
2012-07-18 04:30:00 | 2012-07-18 05:00:00
2012-07-18 05:30:00 | 2012-07-18 06:00:00
これらは基本的に、潜在的な時間枠です。これで、最も長いものを選択するだけの簡単な問題になりました。
ステップ 4:
必要な行は 1 行だけなので、 ORDER BY
/ max/min 選択手法を使用します。LIMIT 1
各ミーティングの終了時間と開始時間の秒差に基づいて順序付けを行い、( を介してLIMIT 1
) 秒数が最も長いものを選択して、最終的に望ましい結果を得ます。
SELECT MAX(b.FromDateTime) FromDateTime,
a.ToDateTime
FROM (
SELECT DISTINCT a.ToDateTime
FROM tbl a
JOIN tbl b ON a.userid <> b.userid
AND a.userid IN (1,2,3,4,5)
AND b.userid IN (1,2,3,4,5)
AND a.ToDateTime > b.FromDateTime
AND a.ToDateTime <= b.ToDateTime
GROUP BY a.userid,
a.FromDateTime,
a.ToDateTime
HAVING COUNT(DISTINCT b.userid) = 4
) a
JOIN (
SELECT DISTINCT a.FromDateTime
FROM tbl a
JOIN tbl b ON a.userid <> b.userid
AND a.userid IN (1,2,3,4,5)
AND b.userid IN (1,2,3,4,5)
AND a.FromDateTime >= b.FromDateTime
AND a.FromDateTime < b.ToDateTime
GROUP BY a.userid,
a.FromDateTime,
a.ToDateTime
HAVING COUNT(DISTINCT b.userid) = 4
) b ON b.FromDateTime < a.ToDateTime
GROUP BY a.ToDateTime
ORDER BY TIMESTAMPDIFF(SECOND, MAX(b.FromDateTime), a.ToDateTime) DESC
LIMIT 1
最終結果の SQLFiddle デモ
他のサンプル データを使用した SQLFiddle デモ
テーブル内のすべてのユーザー間の会議時間を取得する (条件なし):
会議時間を確認するユーザーを指定したくない場合 (テーブル内のすべてのユーザーに対して行うだけです)、次を使用できます。
SELECT MAX(b.FromDateTime) FromDateTime,
a.ToDateTime
FROM (
SELECT DISTINCT a.ToDateTime
FROM tbl a
JOIN tbl b ON a.userid <> b.userid
AND a.ToDateTime > b.FromDateTime
AND a.ToDateTime <= b.ToDateTime
CROSS JOIN (SELECT COUNT(DISTINCT userid) totalusers FROM tbl) c
GROUP BY a.userid,
a.FromDateTime,
a.ToDateTime,
c.totalusers
HAVING COUNT(DISTINCT b.userid) = c.totalusers-1
) a
JOIN (
SELECT DISTINCT a.FromDateTime
FROM tbl a
JOIN tbl b ON a.userid <> b.userid
AND a.FromDateTime >= b.FromDateTime
AND a.FromDateTime < b.ToDateTime
CROSS JOIN (SELECT COUNT(DISTINCT userid) totalusers FROM tbl) c
GROUP BY a.userid,
a.FromDateTime,
a.ToDateTime,
c.totalusers
HAVING COUNT(DISTINCT b.userid) = c.totalusers-1
) b ON b.FromDateTime < a.ToDateTime
GROUP BY a.ToDateTime
ORDER BY TIMESTAMPDIFF(SECOND, MAX(b.FromDateTime), a.ToDateTime) DESC
LIMIT 1