CODE:
CREATE TABLE #Temp1 (CoachID INT, BusyST DATETIME, BusyET DATETIME)
CREATE TABLE #Temp2 (CoachID INT, AvailableST DATETIME, AvailableET DATETIME)
INSERT INTO #Temp1 (CoachID, BusyST, BusyET)
SELECT 1,'2016-08-17 09:12:00','2016-08-17 10:11:00'
UNION
SELECT 3,'2016-08-17 09:30:00','2016-08-17 10:00:00'
UNION
SELECT 4,'2016-08-17 12:07:00','2016-08-17 13:10:00'
INSERT INTO #Temp2 (CoachID, AvailableST, AvailableET)
SELECT 1,'2016-08-17 09:07:00','2016-08-17 11:09:00'
UNION
SELECT 2,'2016-08-17 09:11:00','2016-08-17 09:30:00'
UNION
SELECT 3,'2016-08-17 09:24:00','2016-08-17 13:08:00'
UNION
SELECT 1,'2016-08-17 11:34:00','2016-08-17 12:27:00'
UNION
SELECT 4,'2016-08-17 09:34:00','2016-08-17 13:00:00'
UNION
SELECT 5,'2016-08-17 09:10:00','2016-08-17 09:55:00'
--RESULT-SET QUERY GOES HERE
DROP TABLE #Temp1
DROP TABLE #Temp2
期望的输出:
CoachID CanCoachST CanCoachET NumOfCoaches
1 2016-08-17 09:12:00.000 2016-08-17 09:24:00.000 2 --(ID2 = 2,5)
1 2016-08-17 09:24:00.000 2016-08-17 09:30:00.000 3 --(ID2 = 2,3,5)
1 2016-08-17 09:30:00.000 2016-08-17 09:34:00.000 1 --(ID2 = 5)
1 2016-08-17 09:34:00.000 2016-08-17 09:55:00.000 2 --(ID2 = 4,5)
1 2016-08-17 09:55:00.000 2016-08-17 10:00:00.000 1 --(ID2 = 4)
1 2016-08-17 10:00:00.000 2016-08-17 10:11:00.000 2 --(ID2 = 3,4)
3 2016-08-17 09:30:00.000 2016-08-17 09:34:00.000 1 --(ID2 = 5)
3 2016-08-17 09:34:00.000 2016-08-17 09:55:00.000 2 --(ID2 = 4,5)
3 2016-08-17 09:55:00.000 2016-08-17 10:00:00.000 1 --(ID2 = 4)
4 2016-08-17 12:07:00.000 2016-08-17 12:27:00.000 2 --(ID2 = 1,3)
4 2016-08-17 12:27:00.000 2016-08-17 13:08:00.000 1 --(ID2 = 3)
4 2016-08-17 13:08:00.000 2016-08-17 13:10:00.000 0 --(No one is available)
目标:
将#Temp1
视为团队教练表(ID1)及其会议时间(ST1 =会议开始时间和ET1 =会议结束时间)。
将#Temp2
视为团队教练表(ID2)及其总可用时间(ST2 =可用开始时间和ET2 =可用结束时间)。
现在,我们的目标是找到#Temp2
的所有可能的教练,这些教练可以在#Temp1
的教练会议期间接受教练。
因此,例如,对于教练ID1 = 1,谁在9:12和10:11之间忙碌(数据可以跨越多天,如果该信息很重要),我们有 教练ID2 = 2和5可以在9:12和9:24之间教练 ,教练ID2 = 2,3和5,可以在9:24到9:30之间指导 ,教练ID2 = 5,可以在9:30到9:34之间指导 ,教练ID2 = 4和5,可以在9:34和9:55之间指导 ,教练ID2 = 4,可以在9:55到10:00之间指导 和教练ID2 = 3和4可以在10:00到10:11之间进行指导(注意ID 3如何,虽然在9:24和13:08之间的#Temp2表中可用,但它不能指导ID1 = 1点在9点24分到10点之间,因为它也在9点30分到10点之间忙碌。
到目前为止我的努力:到目前为止只处理打破#Temp1的时间段。还需要弄清楚A)如何从输出中删除非繁忙时间窗口B)添加字段/将其映射到右边的T1 CoachID。
;WITH ED
AS (SELECT BusyET, CoachID FROM #Temp1
UNION ALL
SELECT BusyST, CoachID FROM #Temp1
)
,Brackets
AS (SELECT MIN(BusyST) AS BusyST
,( SELECT MIN(BusyET)
FROM ED e
WHERE e.BusyET > MIN(BusyST)
) AS BusyET
FROM #Temp1 T
UNION ALL
SELECT B.BusyET
,e.BusyET
FROM Brackets B
INNER JOIN ED E ON B.BusyET < E.BusyET
WHERE NOT EXISTS (
SELECT *
FROM ED E2
WHERE E2.BusyET > B.BusyET
AND E2.BusyET < E.BusyET
)
)
SELECT *
FROM Brackets
ORDER BY BusyST;
我想我需要加入比较两个表之间的ST / ET日期,其中ID彼此不匹配。但我很难弄清楚如何实际获得会议时间窗口和唯一计数。
更新了更好的架构/数据集。另请注意,即使CoachID 4未被安排&#34;在最后几分钟,他仍然被列为忙碌状态。并且可能存在这样的情况:在这种情况下没有其他人可以工作,在这种情况下,我们可以返回0 cnt记录(如果它真的很复杂,则不返回它。)
同样,我们的目标是找到所有可用CoachID的计数和组合,以及可以指导忙表中列出的CoachID的可用时间窗口。
更新了更多样本描述匹配样本数据。
答案 0 :(得分:2)
本回答中的查询受到了Itzik Ben-Gan的Packing Intervals的启发。
起初,我并不了解要求的完整复杂性,并认为Table1
和Table2
中的时间间隔不重叠。我认为同一个教练不能同时忙碌和可用。
事实证明我的假设是错误的,因此我在下面留下的查询的第一个变体必须通过初步步骤进行扩展,从Table1
中存储的区间中减去存储在Table2
中的所有区间。 {1}}。
它使用了类似的想法。每个开始的&#34;可用&#34;间隔标记为+1 EventType
,并且&#34;可用&#34; interval用-1 EventType
标记。对于&#34;忙碌&#34;间隔标记反转。 &#34;忙&#34; interval以-1开头,以+1结束。这是在C1_Subtract
。
然后运行总计告诉我们&#34;真正&#34;可用间隔为(C2_Subtract
)。最后,CTE_Available
只留下真正的&#34;可用间隔。
示例数据
我添加了几行来说明如果没有可用的教练会发生什么。我还添加了CoachID=9
,它不在查询的第一个变体的初始结果中。
CREATE TABLE #Temp1 (CoachID INT, BusyST DATETIME, BusyET DATETIME);
CREATE TABLE #Temp2 (CoachID INT, AvailableST DATETIME, AvailableET DATETIME);
-- Start time is inclusive
-- End time is exclusive
INSERT INTO #Temp1 (CoachID, BusyST, BusyET) VALUES
(1, '2016-08-17 09:12:00','2016-08-17 10:11:00'),
(3, '2016-08-17 09:30:00','2016-08-17 10:00:00'),
(4, '2016-08-17 12:07:00','2016-08-17 13:10:00'),
(6, '2016-08-17 15:00:00','2016-08-17 16:00:00'),
(9, '2016-08-17 15:00:00','2016-08-17 16:00:00');
INSERT INTO #Temp2 (CoachID, AvailableST, AvailableET) VALUES
(1,'2016-08-17 09:07:00','2016-08-17 11:09:00'),
(2,'2016-08-17 09:11:00','2016-08-17 09:30:00'),
(3,'2016-08-17 09:24:00','2016-08-17 13:08:00'),
(1,'2016-08-17 11:34:00','2016-08-17 12:27:00'),
(4,'2016-08-17 09:34:00','2016-08-17 13:00:00'),
(5,'2016-08-17 09:10:00','2016-08-17 09:55:00'),
(7,'2016-08-17 15:10:00','2016-08-17 15:20:00'),
(8,'2016-08-17 15:15:00','2016-08-17 15:25:00'),
(7,'2016-08-17 15:40:00','2016-08-17 15:55:00'),
(9,'2016-08-17 15:05:00','2016-08-17 15:07:00'),
(9,'2016-08-17 15:40:00','2016-08-17 16:55:00');
CTE_Available
+---------+-------------------------+-------------------------+
| CoachID | AvailableST | AvailableET |
+---------+-------------------------+-------------------------+
| 1 | 2016-08-17 09:07:00.000 | 2016-08-17 09:12:00.000 |
| 1 | 2016-08-17 10:11:00.000 | 2016-08-17 11:09:00.000 |
| 1 | 2016-08-17 11:34:00.000 | 2016-08-17 12:27:00.000 |
| 2 | 2016-08-17 09:11:00.000 | 2016-08-17 09:30:00.000 |
| 3 | 2016-08-17 09:24:00.000 | 2016-08-17 09:30:00.000 |
| 3 | 2016-08-17 10:00:00.000 | 2016-08-17 13:08:00.000 |
| 4 | 2016-08-17 09:34:00.000 | 2016-08-17 12:07:00.000 |
| 5 | 2016-08-17 09:10:00.000 | 2016-08-17 09:55:00.000 |
| 7 | 2016-08-17 15:10:00.000 | 2016-08-17 15:20:00.000 |
| 7 | 2016-08-17 15:40:00.000 | 2016-08-17 15:55:00.000 |
| 8 | 2016-08-17 15:15:00.000 | 2016-08-17 15:25:00.000 |
| 9 | 2016-08-17 16:00:00.000 | 2016-08-17 16:55:00.000 |
+---------+-------------------------+-------------------------+
现在,我们可以在查询的第一个变体中使用CTE_Available
的这些中间结果,而不是#Temp2
。请参阅查询的第一个变体下面的详细说明。
完整查询
WITH
C1_Subtract
AS
(
SELECT
CoachID
,AvailableST AS ts
,+1 AS EventType
FROM #Temp2
UNION ALL
SELECT
CoachID
,AvailableET AS ts
,-1 AS EventType
FROM #Temp2
UNION ALL
SELECT
CoachID
,BusyST AS ts
,-1 AS EventType
FROM #Temp1
UNION ALL
SELECT
CoachID
,BusyET AS ts
,+1 AS EventType
FROM #Temp1
)
,C2_Subtract AS
(
SELECT
C1_Subtract.*
,SUM(EventType)
OVER (
PARTITION BY CoachID
ORDER BY ts, EventType DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS cnt
,LEAD(ts)
OVER (
PARTITION BY CoachID
ORDER BY ts, EventType DESC)
AS NextTS
FROM C1_Subtract
)
,CTE_Available
AS
(
SELECT
C2_Subtract.CoachID
,C2_Subtract.ts AS AvailableST
,C2_Subtract.NextTS AS AvailableET
FROM C2_Subtract
WHERE cnt > 0
)
,CTE_Intervals
AS
(
SELECT
TBusy.CoachID AS BusyCoachID
,TBusy.BusyST
,TBusy.BusyET
,CA.CoachID AS AvailableCoachID
,CA.AvailableST
,CA.AvailableET
-- max of start time
,CASE WHEN CA.AvailableST < TBusy.BusyST
THEN TBusy.BusyST
ELSE CA.AvailableST
END AS ST
-- min of end time
,CASE WHEN CA.AvailableET > TBusy.BusyET
THEN TBusy.BusyET
ELSE CA.AvailableET
END AS ET
FROM
#Temp1 AS TBusy
CROSS APPLY
(
SELECT
TAvailable.*
FROM
CTE_Available AS TAvailable
WHERE
-- the same coach can't be available and busy
TAvailable.CoachID <> TBusy.CoachID
-- intervals intersect
AND TAvailable.AvailableST < TBusy.BusyET
AND TAvailable.AvailableET > TBusy.BusyST
) AS CA
)
,C1 AS
(
SELECT
BusyCoachID
,AvailableCoachID
,ST AS ts
,+1 AS EventType
FROM CTE_Intervals
UNION ALL
SELECT
BusyCoachID
,AvailableCoachID
,ET AS ts
,-1 AS EventType
FROM CTE_Intervals
UNION ALL
SELECT
CoachID AS BusyCoachID
,CoachID AS AvailableCoachID
,BusyST AS ts
,+1 AS EventType
FROM #Temp1
UNION ALL
SELECT
CoachID AS BusyCoachID
,CoachID AS AvailableCoachID
,BusyET AS ts
,-1 AS EventType
FROM #Temp1
)
,C2 AS
(
SELECT
C1.*
,SUM(EventType)
OVER (
PARTITION BY BusyCoachID
ORDER BY ts, EventType DESC, AvailableCoachID
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
- 1 AS cnt
,LEAD(ts)
OVER (
PARTITION BY BusyCoachID
ORDER BY ts, EventType DESC, AvailableCoachID)
AS NextTS
FROM C1
)
SELECT
BusyCoachID AS CoachID
,ts AS CanCoachST
,NextTS AS CanCoachET
,cnt AS NumOfCoaches
FROM C2
WHERE ts <> NextTS
ORDER BY BusyCoachID, CanCoachST
;
最终结果
+---------+-------------------------+-------------------------+--------------+
| CoachID | CanCoachST | CanCoachET | NumOfCoaches |
+---------+-------------------------+-------------------------+--------------+
| 1 | 2016-08-17 09:12:00.000 | 2016-08-17 09:24:00.000 | 2 |
| 1 | 2016-08-17 09:24:00.000 | 2016-08-17 09:30:00.000 | 3 |
| 1 | 2016-08-17 09:30:00.000 | 2016-08-17 09:34:00.000 | 1 |
| 1 | 2016-08-17 09:34:00.000 | 2016-08-17 09:55:00.000 | 2 |
| 1 | 2016-08-17 09:55:00.000 | 2016-08-17 10:00:00.000 | 1 |
| 1 | 2016-08-17 10:00:00.000 | 2016-08-17 10:11:00.000 | 2 |
| 3 | 2016-08-17 09:30:00.000 | 2016-08-17 09:34:00.000 | 1 |
| 3 | 2016-08-17 09:34:00.000 | 2016-08-17 09:55:00.000 | 2 |
| 3 | 2016-08-17 09:55:00.000 | 2016-08-17 10:00:00.000 | 1 |
| 4 | 2016-08-17 12:07:00.000 | 2016-08-17 12:27:00.000 | 2 |
| 4 | 2016-08-17 12:27:00.000 | 2016-08-17 13:08:00.000 | 1 |
| 4 | 2016-08-17 13:08:00.000 | 2016-08-17 13:10:00.000 | 0 |
| 6 | 2016-08-17 15:00:00.000 | 2016-08-17 15:10:00.000 | 0 |
| 6 | 2016-08-17 15:10:00.000 | 2016-08-17 15:15:00.000 | 1 |
| 6 | 2016-08-17 15:15:00.000 | 2016-08-17 15:20:00.000 | 2 |
| 6 | 2016-08-17 15:20:00.000 | 2016-08-17 15:25:00.000 | 1 |
| 6 | 2016-08-17 15:25:00.000 | 2016-08-17 15:40:00.000 | 0 |
| 6 | 2016-08-17 15:40:00.000 | 2016-08-17 15:55:00.000 | 1 |
| 6 | 2016-08-17 15:55:00.000 | 2016-08-17 16:00:00.000 | 0 |
| 9 | 2016-08-17 15:00:00.000 | 2016-08-17 15:10:00.000 | 0 |
| 9 | 2016-08-17 15:10:00.000 | 2016-08-17 15:15:00.000 | 1 |
| 9 | 2016-08-17 15:15:00.000 | 2016-08-17 15:20:00.000 | 2 |
| 9 | 2016-08-17 15:20:00.000 | 2016-08-17 15:25:00.000 | 1 |
| 9 | 2016-08-17 15:25:00.000 | 2016-08-17 15:40:00.000 | 0 |
| 9 | 2016-08-17 15:40:00.000 | 2016-08-17 15:55:00.000 | 1 |
| 9 | 2016-08-17 15:55:00.000 | 2016-08-17 16:00:00.000 | 0 |
+---------+-------------------------+-------------------------+--------------+
我建议创建以下索引以避免执行计划中的某些排序。
CREATE UNIQUE NONCLUSTERED INDEX [IX_CoachID_BusyST] ON #Temp1
(
CoachID ASC,
BusyST ASC
);
CREATE UNIQUE NONCLUSTERED INDEX [IX_CoachID_BusyET] ON #Temp1
(
CoachID ASC,
BusyET ASC
);
CREATE UNIQUE NONCLUSTERED INDEX [IX_CoachID_AvailableST] ON #Temp2
(
CoachID ASC,
AvailableST ASC
);
CREATE UNIQUE NONCLUSTERED INDEX [IX_CoachID_AvailableET] ON #Temp2
(
CoachID ASC,
AvailableET ASC
);
在实际数据上,瓶颈可能在其他地方,这可能取决于数据分布。查询相当复杂,如果没有实际数据进行调整就会产生过多的猜测。
逐步运行查询,CTE-by-CTE并检查中间结果以查明其工作原理。
CTE_Intervals
为我们提供了一个与繁忙间隔相交的可用间隔列表。
C1
将开始和结束时间都放在与相应EventType
相同的列中。这将帮助我们跟踪间隔开始或结束的时间。
总计EventType
计算可用的教练数。 C1
工会忙于教练,在没有教练的情况下正确统计案件。
WITH
CTE_Intervals
AS
(
SELECT
TBusy.CoachID AS BusyCoachID
,TBusy.BusyST
,TBusy.BusyET
,CA.CoachID AS AvailableCoachID
,CA.AvailableST
,CA.AvailableET
-- max of start time
,CASE WHEN CA.AvailableST < TBusy.BusyST
THEN TBusy.BusyST
ELSE CA.AvailableST
END AS ST
-- min of end time
,CASE WHEN CA.AvailableET > TBusy.BusyET
THEN TBusy.BusyET
ELSE CA.AvailableET
END AS ET
FROM
#Temp1 AS TBusy
CROSS APPLY
(
SELECT
TAvailable.*
FROM
#Temp2 AS TAvailable
WHERE
-- the same coach can't be available and busy
TAvailable.CoachID <> TBusy.CoachID
-- intervals intersect
AND TAvailable.AvailableST < TBusy.BusyET
AND TAvailable.AvailableET > TBusy.BusyST
) AS CA
)
,C1 AS
(
SELECT
BusyCoachID
,AvailableCoachID
,ST AS ts
,+1 AS EventType
FROM CTE_Intervals
UNION ALL
SELECT
BusyCoachID
,AvailableCoachID
,ET AS ts
,-1 AS EventType
FROM CTE_Intervals
UNION ALL
SELECT
CoachID AS BusyCoachID
,CoachID AS AvailableCoachID
,BusyST AS ts
,+1 AS EventType
FROM #Temp1
UNION ALL
SELECT
CoachID AS BusyCoachID
,CoachID AS AvailableCoachID
,BusyET AS ts
,-1 AS EventType
FROM #Temp1
)
,C2 AS
(
SELECT
C1.*
,SUM(EventType)
OVER (
PARTITION BY BusyCoachID
ORDER BY ts, EventType DESC, AvailableCoachID
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
- 1 AS cnt
,LEAD(ts)
OVER (
PARTITION BY BusyCoachID
ORDER BY ts, EventType DESC, AvailableCoachID)
AS NextTS
FROM C1
)
SELECT
BusyCoachID AS CoachID
,ts AS CanCoachST
,NextTS AS CanCoachET
,cnt AS NumOfCoaches
FROM C2
WHERE ts <> NextTS
ORDER BY BusyCoachID, CanCoachST
;
DROP TABLE #Temp1;
DROP TABLE #Temp2;
<强>结果强>
我为每一行添加了评论,其中包含可用教练的ID。
现在我理解为什么我的初步结果与预期结果不一样。
+---------+---------------------+---------------------+--------------+
| CoachID | CanCoachST | CanCoachET | NumOfCoaches |
+---------+---------------------+---------------------+--------------+
| 1 | 2016-08-17 09:12:00 | 2016-08-17 09:24:00 | 2 | 2,5
| 1 | 2016-08-17 09:24:00 | 2016-08-17 09:30:00 | 3 | 2,3,5
| 1 | 2016-08-17 09:30:00 | 2016-08-17 09:34:00 | 2 | 3,5
| 1 | 2016-08-17 09:34:00 | 2016-08-17 09:55:00 | 3 | 3,4,5
| 1 | 2016-08-17 09:55:00 | 2016-08-17 10:11:00 | 2 | 3,4
| 3 | 2016-08-17 09:30:00 | 2016-08-17 09:34:00 | 2 | 1,5
| 3 | 2016-08-17 09:34:00 | 2016-08-17 09:55:00 | 3 | 1,4,5
| 3 | 2016-08-17 09:55:00 | 2016-08-17 10:00:00 | 2 | 1,4
| 4 | 2016-08-17 12:07:00 | 2016-08-17 12:27:00 | 2 | 3,1
| 4 | 2016-08-17 12:27:00 | 2016-08-17 13:08:00 | 1 | 3
| 4 | 2016-08-17 13:08:00 | 2016-08-17 13:10:00 | 0 | none
| 6 | 2016-08-17 15:00:00 | 2016-08-17 15:10:00 | 0 | none
| 6 | 2016-08-17 15:10:00 | 2016-08-17 15:15:00 | 1 | 7
| 6 | 2016-08-17 15:15:00 | 2016-08-17 15:20:00 | 2 | 7,8
| 6 | 2016-08-17 15:20:00 | 2016-08-17 15:25:00 | 1 | 8
| 6 | 2016-08-17 15:25:00 | 2016-08-17 15:40:00 | 0 | none
| 6 | 2016-08-17 15:40:00 | 2016-08-17 15:55:00 | 1 | 7
| 6 | 2016-08-17 15:55:00 | 2016-08-17 16:00:00 | 0 | none
+---------+---------------------+---------------------+--------------+
答案 1 :(得分:1)
我能说的最好,你要找的是这样的:
<span id="numactions">5</span>
<button type="button" onClick="clickME()">Click me</button>
<script>
var clicks = document.getElementById("numactions");
function clickME() {
clicks -= 1;
document.getElementById("numactions").innerHTML = clicks;
}
</script>
CTE会将你的#Temp1教练分成半小时的时段,然后我假设你想找到#Temp2中所有不是同一个ID并且开始转换的人更早或同时或在同一时间结束...... 注意:我假设这里的街区只能是半小时。
编辑:没关系......我刚刚意识到你也想要从结果集中对#Temp1中的繁忙人员进行折扣,所以我在申请中添加了一个not exists子句...
答案 2 :(得分:1)
此查询将执行计算:
SELECT TT1.ID1
, case when TT2.ST2 < TT1.ST1 THEN TT1.ST1 ELSE TT2.ST2 END
, case when TT2.ET2 > TT1.ET1 THEN TT1.ET1 ELSE TT2.ET2 END
, COUNT(distinct TT2.id2)
FROM #Temp1 TT1 INNER JOIN #Temp2 TT2
ON TT1.ET1 > TT2.ST2 AND TT1.ST1 < TT2.ET2 AND TT1.ID1 <> TT2.ID2
GROUP BY TT1.ID1
, case when TT2.ST2 < TT1.ST1 THEN TT1.ST1 ELSE TT2.ST2 END
, case when TT2.ET2 > TT1.ET1 THEN TT1.ET1 ELSE TT2.ET2 END
然而,结果将包括教练cal填写整个时段的位置,例如对于教练1将有三个位置:从9:00到9:30,替补教练#2,从9:替补教练#4和时间段从上午9点到10点用替补教练#3和#4替换。这是整个结果:
ID1
----------- ----------------------- ----------------------- -----------
1 2016-08-17 09:00:00.000 2016-08-17 09:30:00.000 1
1 2016-08-17 09:00:00.000 2016-08-17 10:00:00.000 2
1 2016-08-17 09:30:00.000 2016-08-17 10:00:00.000 1
3 2016-08-17 09:30:00.000 2016-08-17 10:00:00.000 3
4 2016-08-17 12:00:00.000 2016-08-17 12:30:00.000 1
4 2016-08-17 12:00:00.000 2016-08-17 13:00:00.000 1
答案 3 :(得分:1)
这是您考虑到与可用教练重叠的繁忙教练的预期结果。
| CoachID | CanCoachST | CanCoachET | NumOfCoaches | CanCoach |
|---------|------------------|------------------|--------------|----------|
| 1 | 2016-08-17 09:12 | 2016-08-17 09:24 | 2 | 2, 5 |
| 1 | 2016-08-17 09:24 | 2016-08-17 09:30 | 3 | 2, 3, 5 |
| 1 | 2016-08-17 09:30 | 2016-08-17 09:34 | 1 | 5 |
| 1 | 2016-08-17 09:34 | 2016-08-17 09:55 | 2 | 4, 5 |
| 1 | 2016-08-17 09:55 | 2016-08-17 10:00 | 1 | 4 |
| 1 | 2016-08-17 10:00 | 2016-08-17 10:11 | 2 | 3, 4 |
| 3 | 2016-08-17 09:30 | 2016-08-17 09:34 | 1 | 5 |
| 3 | 2016-08-17 09:34 | 2016-08-17 09:55 | 2 | 4, 5 |
| 3 | 2016-08-17 09:55 | 2016-08-17 10:00 | 1 | 4 |
| 4 | 2016-08-17 12:07 | 2016-08-17 12:27 | 2 | 1, 3 |
| 4 | 2016-08-17 12:27 | 2016-08-17 13:08 | 1 | 3 |
| 4 | 2016-08-17 13:08 | 2016-08-17 13:10 | 0 | NULL |
#Temp1
作为忙碌的教练:
| CoachID | BusyST | BusyET |
|---------|------------------|------------------|
| 1 | 2016-08-17 09:12 | 2016-08-17 10:11 |
| 3 | 2016-08-17 09:30 | 2016-08-17 10:00 |
| 4 | 2016-08-17 12:07 | 2016-08-17 13:10 |
#Temp2
作为可用教练:
| CoachID | AvailableST | AvailableET |
|---------|------------------|------------------|
| 1 | 2016-08-17 09:07 | 2016-08-17 11:09 |
| 1 | 2016-08-17 11:34 | 2016-08-17 12:27 |
| 2 | 2016-08-17 09:11 | 2016-08-17 09:30 |
| 3 | 2016-08-17 09:24 | 2016-08-17 13:08 |
| 4 | 2016-08-17 09:34 | 2016-08-17 13:00 |
| 5 | 2016-08-17 09:10 | 2016-08-17 09:55 |
下面的脚本有点长。
;
with
st
(
CoachID,
CanCoachST
)
as
(
select
bound.CoachID,
s.BusyST
from
#Temp1 as s
cross apply
(
select
b.CoachID,
b.BusyST,
b.BusyET
from
#Temp1 as b
where 1 = 1
and s.BusyST between b.BusyST and b.BusyET
)
as bound
union all
select
bound.CoachID,
s.BusyET
from
#Temp1 as s
cross apply
(
select
b.CoachID,
b.BusyST,
b.BusyET
from
#Temp1 as b
where 1 = 1
and s.BusyET between b.BusyST and b.BusyET
and s.CoachID != b.CoachID
)
as bound
union all
select
bound.CoachID,
s.AvailableST
from
#Temp2 as s
cross apply
(
select
b.CoachID,
b.BusyST,
b.BusyET
from
#Temp1 as b
where 1 = 1
and s.AvailableST between b.BusyST and b.BusyET
)
as bound
union all
select
bound.CoachID,
s.AvailableET
from
#Temp2 as s
cross apply
(
select
b.CoachID,
b.BusyST,
b.BusyET
from
#Temp1 as b
where 1 = 1
and s.AvailableET between b.BusyST and b.BusyET
and s.CoachID != b.CoachID
)
as bound
),
d as
(
select distinct
CoachID,
CanCoachST
from
st
),
r as
(
select
row_number() over (order by CoachID, CanCoachST) as RowID,
CoachID,
CanCoachST
from
d
),
rng as
(
select
r1.RowID,
r1.CoachID,
r1.CanCoachST,
case when r1.CoachID = r2.CoachID
then r2.CanCoachST else t.BusyET end as CanCoachET
from
r as r1
left join
r as r2
on
r1.RowID = r2.RowID - 1
left join
#Temp1 as t
on
t.CoachID = r1.CoachID
),
c as
(
select
rng.RowID,
rng.CoachID,
rng.CanCoachST,
rng.CanCoachET,
t2.CoachID as CanCoachID
from
rng
cross join
#Temp1 as t1
cross join
#Temp2 as t2
where 1 = 1
and t2.CoachID != rng.CoachID
and t2.AvailableST <= rng.CanCoachST
and t2.AvailableET >= rng.CanCoachET
),
b as
(
select
rng.RowID,
rng.CoachID,
rng.CanCoachST,
rng.CanCoachET,
t1.CoachID as BusyCoachID
from
rng
cross join
#Temp1 as t1
where 1 = 1
and t1.CoachID != rng.CoachID
and t1.BusyST <= rng.CanCoachST
and t1.BusyET >= rng.CanCoachET
),
e as
(
select
c.RowID,
c.CoachID,
c.CanCoachST,
c.CanCoachET,
c.CanCoachID
from
c
except
select
b.RowID,
b.CoachID,
b.CanCoachST,
b.CanCoachET,
b.BusyCoachID
from
b
),
f as
(
select
rng.RowID,
rng.CoachID,
rng.CanCoachST,
rng.CanCoachET,
e.CanCoachID
from
rng
left join
e
on
e.RowID = rng.RowID
)
select
f.CoachID,
f.CanCoachST,
f.CanCoachET,
count(f.CanCoachID) as NumOfCoaches,
stuff
(
(
select ', ' + cast(f1.CanCoachID as varchar(5))
from f as f1 where f1.RowID = f.RowID
for xml path('')
),
1, 2, ''
)
as CanCoach
from
f
group by
f.RowID,
f.CoachID,
f.CanCoachST,
f.CanCoachET
order by
1, 2
答案 4 :(得分:1)
我建议使用区间/时间表的概念。 另一种解释方法是考虑“时间维度表”
定义所有时间,然后以您关注的粒度参考时间间隔记录您的事实。因为你有时间在7分钟和11分钟结束,我选择1分钟间隔,但我建议间隔15-30分钟。
通过这样做,可以很容易地加入/比较表格。
考虑以下设计/实施:
- 维度表
-- drop table #intervals
create table #intervals(intervalId int identity(1,1) not null primary key clustered,intervalStartTime datetime unique)
declare @s datetime, @e datetime, @i int
set @s = '2016-08-16'
set @e = '2016-08-18'
set @i = 1
while (@s <= @e )
begin
insert into #intervals(intervalStartTime) values(@s)
set @s = dateadd(minute, @i, @s)
end
- 事实表:
-- drop table #Fact
create table #Fact(intervalId int,coachid int,isBusy int default(0),isAvailable int default(0))
- 记录每个教练的时间
insert into #Fact(coachid,intervalId)
select distinct c.coachid, i.intervalId
from
(
select distinct coachid from #temp1
union
select distinct coachid from #temp2
) c cross join #intervals i
-- record free / busy info
update f set isbusy = 1
from #intervals i inner join #fact f on i.intervalId = f.intervalId
inner join #temp1 t on f.coachid = t.coachid and i.intervalStartTime between t.BusyST and t.BusyET
-- record free / busy info
update f set isAvailable = 1
from #intervals i inner join #fact f on i.intervalId = f.intervalId
inner join #temp2 t on f.coachid = t.coachid and i.intervalStartTime between t.AvailableST and t.AvailableET
- 构建查询以查找常见时间等
select * from #intervals i inner join #Fact f on i.intervalId = f.intervalId
- 示例结果显示可用的教练数与免费
选择i.intervalId,i.intervalStartTime,sum(isBusy)作为coachesBusy,sum(isAvailable)作为coachesAvailable 来自#intervals i inner join #Fact f on i.intervalId = f.intervalId group by i.intervalId,i.intervalStartTime 总和(isBusy)&lt;总和(isAvailable)
然后,您可以根据需要查找常用或唯一的间隔ID。
如果您需要进一步说明,请与我们联系。
答案 5 :(得分:1)
我使用的是一个小数字表...你不需要日期,只需数字。我在这里构建的内容比在实际场景中使用的内容要小。
CREATE TABLE dbo.Numbers (Num INT PRIMARY KEY CLUSTERED);
WITH E1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS t(N))
,E2 AS (SELECT N = 1 FROM E1 AS a, E1 AS b)
,E4 AS (SELECT N = 1 FROM E2 AS a, E2 AS b)
,cteTally AS (SELECT N = 0 UNION ALL
SELECT N = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4)
INSERT INTO dbo.Numbers (Num)
SELECT N FROM cteTally;
请注意下面的@startDate ...人为地接近您正在处理的日期,并且在一个真实的产品场景中,您可以更早地将该日期与更大的Numbers表一起使用。
以下是您的问题的解决方案,它将适用于较旧的SQL Server版本(以及您已标记的2012):
DECLARE @startDate DATETIME = '20160817';
WITH cteBusy AS
(
SELECT num.Num
, busy.CoachID
FROM #Temp1 AS busy
JOIN dbo.Numbers AS num
ON num.Num >= DATEDIFF(MINUTE, @startDate, busy.BusyST)
AND num.Num < DATEDIFF(MINUTE, @startDate, busy.BusyET)
)
, cteAvailable AS
(
SELECT num.Num
, avail.CoachID
FROM #Temp2 AS avail
JOIN dbo.Numbers AS num
ON num.Num >= DATEDIFF(MINUTE, @startDate, avail.AvailableST)
AND num.Num < DATEDIFF(MINUTE, @startDate, avail.AvailableET)
LEFT JOIN cteBusy AS b
ON b.Num = num.Num
AND b.CoachID = avail.CoachID
WHERE b.Num IS NULL
)
, cteGrouping AS
(
SELECT b.Num
, b.CoachID
, NumOfCoaches = COUNT(a.CoachID)
FROM cteBusy AS b
LEFT JOIN cteAvailable AS a
ON a.Num = b.Num
GROUP BY b.Num, b.CoachID
)
, cteFinal AS
(
SELECT cte.Num
, cte.CoachID
, cte.NumOfCoaches
, block = cte.Num - ROW_NUMBER() OVER(PARTITION BY cte.CoachID, cte.NumOfCoaches ORDER BY cte.Num)
FROM cteGrouping AS cte
)
SELECT cte.CoachID
, CanCoachST = DATEADD(MINUTE, MIN(cte.Num), @startDate)
, CanCoachET = DATEADD(MINUTE, MAX(cte.Num) + 1, @startDate)
, cte.NumOfCoaches
FROM cteFinal AS cte
GROUP BY cte.CoachId, cte.NumOfCoaches, cte.block
ORDER BY cte.CoachID, CanCoachST;
享受!
答案 6 :(得分:1)
我相信以下查询会有效,但我对性能没有任何承诺。
var WebPartElementID ="ctl00_ctl34_g_db6615a7_4c3b_4a14_9bbc_43ce9d63c24d_FormControl0";
var WebPartData = document.getElementById(WebPartElementID);
WebPartData.onkeypress = stopRKey;
function stopRKey(evt) {
var evt = (evt) ? evt : ((event) ? event : null);
var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
alert(evt.keyCode);
if ((evt.keyCode == 13)) { document.getElementById("ctl00_ctl34_g_db6615a7_4c3b_4a14_9bbc_43ce9d63c24d_FormControl0_V1_I1_R1_I1_T4").focus(); return false; }
}