您可以使用此解决方案找到符合您条件的所有 用户(例如userids 1-5)可以满足的“最佳”时间窗口。 “最佳”时间窗口以最大秒数衡量。
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
COUNT(DISTINCT... 之后的 4 只是您的条件中的用户数减一(因为用户无法加入自己)。相应调整。
我们应该返回的是所有用户都可以参加的会议的开始和结束时间。
查询细分
给定以下数据:
(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 到 2012-07-18 2:00:00,所以我们的预期结果应该是:
FromDateTime | ToDateTime
----------------------------------------
2012-07-18 1:10:00 | 2012-07-18 2:00:00
第 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 Demo
第 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 代表每个潜在会议窗口的开始。我们只想根据ToDateTime 提取FromDateTime 最新的行。我们在下一步中使用GROUP BY 和MAX() 聚合函数来执行此操作。
SQLFiddle Demo
第三步:
接下来,我们在ToDateTime 上使用GROUP BY 并在FromDateTime 上使用MAX() 来仅提取最新的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
这些基本上是我们潜在的时间窗口。现在只需选择最长的一个即可。
第四步:
我们使用ORDER BY / 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 Demo of Final Result
SQLFiddle Demo with Other Example Data
获取表中所有用户的会议时间(无条件):
如果您不想指定要检查哪些用户的会议时间(只需为表中的所有用户执行此操作),您可以使用:
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