根据 SQL 的可用性选择时间 windows
Selecting time windows based on availability in SQL
我有一个包含多个时间 windows 的数据集,一个可用性指示器和一个优先级索引。
创建数据集:
CREATE TABLE TimeWindows(
TimeFrom DATETIME NOT NULL,
TimeTo DATETIME NOT NULL,
Priority INT NOT NULL,
Available BIT NOT NULL
);
INSERT INTO TimeWindows(TimeFrom, TimeTo, Priority, Available) VALUES
('2017-07-22 07:00:00', '2017-07-22 12:00:00', 1, 1),
('2017-07-22 13:00:00', '2017-07-22 17:00:00', 1, 1),
('2017-07-22 12:30:00', '2017-07-23 00:00:00', 3, 0),
('2017-07-23 07:00:00', '2017-07-23 12:00:00', 1, 1),
('2017-07-23 13:00:00', '2017-07-23 17:00:00', 1, 1),
('2017-07-23 00:00:00', '2017-07-24 00:00:00', 2, 0),
('2017-07-23 19:00:00', '2017-07-23 20:00:00', 4, 1),
('2017-07-24 07:00:00', '2017-07-24 12:00:00', 1, 1),
('2017-07-24 13:00:00', '2017-07-24 17:00:00', 1, 1),
('2017-07-24 15:00:00', '2017-07-24 16:00:00', 4, 0);
示例数据集:
| TimeFrom | TimeTo | Priority | Available |
|---------------------|---------------------|----------|-----------|
| 2017-07-22 07:00:00 | 2017-07-22 12:00:00 | 1 | 1 |
| 2017-07-22 13:00:00 | 2017-07-22 17:00:00 | 1 | 1 |
| 2017-07-22 12:30:00 | 2017-07-23 00:00:00 | 3 | 0 |
| 2017-07-23 07:00:00 | 2017-07-23 12:00:00 | 1 | 1 |
| 2017-07-23 13:00:00 | 2017-07-23 17:00:00 | 1 | 1 |
| 2017-07-23 00:00:00 | 2017-07-24 00:00:00 | 2 | 0 |
| 2017-07-23 19:00:00 | 2017-07-24 20:00:00 | 4 | 1 |
| 2017-07-24 07:00:00 | 2017-07-24 12:00:00 | 1 | 1 |
| 2017-07-24 13:00:00 | 2017-07-24 17:00:00 | 1 | 1 |
| 2017-07-24 15:00:00 | 2017-07-24 16:00:00 | 4 | 0 |
问题:
我想生成一组新的时间windows,它只代表可用的时间段。
业务规则:
- 具有较高优先级的时间windows,将否决具有较低优先级
的冲突时间windows
- 可用性指示器如果可用则设置为 1,如果不可用则设置为 0
- 重叠时间windows需要合并
- 一个可用时间window被'split'一段时间的不可用,将转换成两个单独的可用时间windows
想要的结果:
| TimeFrom | TimeTo |
|---------------------|---------------------|
| 2017-07-22 07:00:00 | 2017-07-22 12:00:00 |
| 2017-07-23 19:00:00 | 2017-07-23 20:00:00 |
| 2017-07-24 07:00:00 | 2017-07-24 12:00:00 |
| 2017-07-24 13:00:00 | 2017-07-24 15:00:00 |
| 2017-07-24 16:00:00 | 2017-07-24 17:00:00 |
谁能告诉我如何解决 SQL 中的这个问题?
在此先感谢您的帮助。
好吧,我已经有一段时间没解决好 sql 挑战了,所以我为您完成了困难的部分。使用带有一些 case 语句的 CTE 和带有更多 case 语句的另一个查询来获取除一个之外的所有业务规则 -
Overlapping time windows need to be consolidated.
但这确实是最简单的部分。您会在解释逻辑的代码中看到很多注释,当然,我只能根据您的示例数据进行自我测试,但它应该可以帮助您入门。
可能还有其他更好的解决方案,但这是我想出的:
;WITH CTE AS
(
SELECT t0.TimeFrom As AvailableFrom,
t0.TimeTo As AvailableTo,
t1.TimeFrom As UnavailableFrom,
t1.TimeTo As UnavailableTo,
CASE WHEN t1.Available IS NULL THEN
-- no overlapping records with higher priority and available = 0, use t0 start and end.
1 -- t0.TimeFrom, t0.TimeTo
ELSE
CASE
WHEN t0.TimeFrom > t1.TimeFrom AND t0.TimeTo < t1.TimeTo THEN
-- t0 is inside t1, record of t0 can't be used.
0
WHEN t0.TimeFrom < t1.TimeFrom AND t0.TimeTo < t1.TimeTo THEN
-- t0 starts before t1 starts, and also ends before t1 ends. this means that the start will be t0 start.
2 -- t0.TimeFrom, t1.TimeFrom
WHEN t0.TimeFrom > t1.TimeFrom AND t0.TimeTo > t1.TimeTo THEN
-- t0 starts after t1, but also ends after it. this means that the start will be t1 end.
3 -- t1.TimeTo, t0.TimeTo
WHEN t0.TimeFrom < t1.TimeFrom AND t0.TimeTo > t1.TimeTo THEN
-- t1 is inside t0, need to create 2 records for this.
4 -- 2 records - t0.TimeFrom, t1.TimeFrom and also t1.TimeTo and t0.TimeTo
END
END As RecordType
FROM TimeWindows t0
LEFT JOIN TimeWindows t1 ON t0.TimeFrom <= t1.TimeTo -- t1 overlaps t0
AND t0.TimeTo >= t1.TimeFrom -- t1 overlaps t0
AND t0.Priority < t1.Priority -- t1 priority is higher than t0 priority
AND t1.Available = 0 -- t1 records are unavaialbe
WHERE t0.Available = 1 -- t0 records are available
-- t0 holds all the available time slots,
-- while t1 holds all the unavailable time slots that overlap t0 records and have a higher priority. (otherwise they don't matter...)
)
SELECT CASE RecordType
WHEN 1 THEN
AvailableFrom
WHEN 2 THEN
AvailableFrom
WHEN 3 THEN
UnavailableTo
WHEN 4 THEN
AvailableFrom
END As TimeFrom,
CASE RecordType
WHEN 1 THEN
AvailableTo
WHEN 2 THEN
UnavailableFrom
WHEN 3 THEN
AvailableTo
WHEN 4 THEN
UnavailableFrom
END As TimeTo
FROM CTE
WHERE RecordType > 0
UNION ALL
SELECT UnavailableTo,
AvailableTo
FROM CTE
WHERE RecordType =4
结果:
TimeFrom TimeTo
22.07.2017 07:00:00 22.07.2017 12:00:00
23.07.2017 19:00:00 23.07.2017 20:00:00
24.07.2017 07:00:00 24.07.2017 12:00:00
24.07.2017 13:00:00 24.07.2017 15:00:00
24.07.2017 16:00:00 24.07.2017 17:00:00
我有一个包含多个时间 windows 的数据集,一个可用性指示器和一个优先级索引。
创建数据集:
CREATE TABLE TimeWindows(
TimeFrom DATETIME NOT NULL,
TimeTo DATETIME NOT NULL,
Priority INT NOT NULL,
Available BIT NOT NULL
);
INSERT INTO TimeWindows(TimeFrom, TimeTo, Priority, Available) VALUES
('2017-07-22 07:00:00', '2017-07-22 12:00:00', 1, 1),
('2017-07-22 13:00:00', '2017-07-22 17:00:00', 1, 1),
('2017-07-22 12:30:00', '2017-07-23 00:00:00', 3, 0),
('2017-07-23 07:00:00', '2017-07-23 12:00:00', 1, 1),
('2017-07-23 13:00:00', '2017-07-23 17:00:00', 1, 1),
('2017-07-23 00:00:00', '2017-07-24 00:00:00', 2, 0),
('2017-07-23 19:00:00', '2017-07-23 20:00:00', 4, 1),
('2017-07-24 07:00:00', '2017-07-24 12:00:00', 1, 1),
('2017-07-24 13:00:00', '2017-07-24 17:00:00', 1, 1),
('2017-07-24 15:00:00', '2017-07-24 16:00:00', 4, 0);
示例数据集:
| TimeFrom | TimeTo | Priority | Available |
|---------------------|---------------------|----------|-----------|
| 2017-07-22 07:00:00 | 2017-07-22 12:00:00 | 1 | 1 |
| 2017-07-22 13:00:00 | 2017-07-22 17:00:00 | 1 | 1 |
| 2017-07-22 12:30:00 | 2017-07-23 00:00:00 | 3 | 0 |
| 2017-07-23 07:00:00 | 2017-07-23 12:00:00 | 1 | 1 |
| 2017-07-23 13:00:00 | 2017-07-23 17:00:00 | 1 | 1 |
| 2017-07-23 00:00:00 | 2017-07-24 00:00:00 | 2 | 0 |
| 2017-07-23 19:00:00 | 2017-07-24 20:00:00 | 4 | 1 |
| 2017-07-24 07:00:00 | 2017-07-24 12:00:00 | 1 | 1 |
| 2017-07-24 13:00:00 | 2017-07-24 17:00:00 | 1 | 1 |
| 2017-07-24 15:00:00 | 2017-07-24 16:00:00 | 4 | 0 |
问题:
我想生成一组新的时间windows,它只代表可用的时间段。
业务规则:
- 具有较高优先级的时间windows,将否决具有较低优先级 的冲突时间windows
- 可用性指示器如果可用则设置为 1,如果不可用则设置为 0
- 重叠时间windows需要合并
- 一个可用时间window被'split'一段时间的不可用,将转换成两个单独的可用时间windows
想要的结果:
| TimeFrom | TimeTo |
|---------------------|---------------------|
| 2017-07-22 07:00:00 | 2017-07-22 12:00:00 |
| 2017-07-23 19:00:00 | 2017-07-23 20:00:00 |
| 2017-07-24 07:00:00 | 2017-07-24 12:00:00 |
| 2017-07-24 13:00:00 | 2017-07-24 15:00:00 |
| 2017-07-24 16:00:00 | 2017-07-24 17:00:00 |
谁能告诉我如何解决 SQL 中的这个问题?
在此先感谢您的帮助。
好吧,我已经有一段时间没解决好 sql 挑战了,所以我为您完成了困难的部分。使用带有一些 case 语句的 CTE 和带有更多 case 语句的另一个查询来获取除一个之外的所有业务规则 -
Overlapping time windows need to be consolidated.
但这确实是最简单的部分。您会在解释逻辑的代码中看到很多注释,当然,我只能根据您的示例数据进行自我测试,但它应该可以帮助您入门。
可能还有其他更好的解决方案,但这是我想出的:
;WITH CTE AS
(
SELECT t0.TimeFrom As AvailableFrom,
t0.TimeTo As AvailableTo,
t1.TimeFrom As UnavailableFrom,
t1.TimeTo As UnavailableTo,
CASE WHEN t1.Available IS NULL THEN
-- no overlapping records with higher priority and available = 0, use t0 start and end.
1 -- t0.TimeFrom, t0.TimeTo
ELSE
CASE
WHEN t0.TimeFrom > t1.TimeFrom AND t0.TimeTo < t1.TimeTo THEN
-- t0 is inside t1, record of t0 can't be used.
0
WHEN t0.TimeFrom < t1.TimeFrom AND t0.TimeTo < t1.TimeTo THEN
-- t0 starts before t1 starts, and also ends before t1 ends. this means that the start will be t0 start.
2 -- t0.TimeFrom, t1.TimeFrom
WHEN t0.TimeFrom > t1.TimeFrom AND t0.TimeTo > t1.TimeTo THEN
-- t0 starts after t1, but also ends after it. this means that the start will be t1 end.
3 -- t1.TimeTo, t0.TimeTo
WHEN t0.TimeFrom < t1.TimeFrom AND t0.TimeTo > t1.TimeTo THEN
-- t1 is inside t0, need to create 2 records for this.
4 -- 2 records - t0.TimeFrom, t1.TimeFrom and also t1.TimeTo and t0.TimeTo
END
END As RecordType
FROM TimeWindows t0
LEFT JOIN TimeWindows t1 ON t0.TimeFrom <= t1.TimeTo -- t1 overlaps t0
AND t0.TimeTo >= t1.TimeFrom -- t1 overlaps t0
AND t0.Priority < t1.Priority -- t1 priority is higher than t0 priority
AND t1.Available = 0 -- t1 records are unavaialbe
WHERE t0.Available = 1 -- t0 records are available
-- t0 holds all the available time slots,
-- while t1 holds all the unavailable time slots that overlap t0 records and have a higher priority. (otherwise they don't matter...)
)
SELECT CASE RecordType
WHEN 1 THEN
AvailableFrom
WHEN 2 THEN
AvailableFrom
WHEN 3 THEN
UnavailableTo
WHEN 4 THEN
AvailableFrom
END As TimeFrom,
CASE RecordType
WHEN 1 THEN
AvailableTo
WHEN 2 THEN
UnavailableFrom
WHEN 3 THEN
AvailableTo
WHEN 4 THEN
UnavailableFrom
END As TimeTo
FROM CTE
WHERE RecordType > 0
UNION ALL
SELECT UnavailableTo,
AvailableTo
FROM CTE
WHERE RecordType =4
结果:
TimeFrom TimeTo
22.07.2017 07:00:00 22.07.2017 12:00:00
23.07.2017 19:00:00 23.07.2017 20:00:00
24.07.2017 07:00:00 24.07.2017 12:00:00
24.07.2017 13:00:00 24.07.2017 15:00:00
24.07.2017 16:00:00 24.07.2017 17:00:00