如何找到重叠的日期范围之间的其余部分
How to find the rest between date ranges that overlaps
我有以下mysqltable(房间)
id_user_room username room_name date_from date_to
1 mike A1 2017-02-01 2017-02-10
2 evans A2 2017-01-20 2017-01-30
3 mike A3 2017-02-16 2017-02-20
4 mike A1 2017-03-01 2017-03-18
如果我想知道 Mike 在 2017-01-01 和 2017-05-30 之间的房间,我执行以下 SQL 查询。
SELECT room_name, date_from, date_to
FROM rooms
WHERE (date_to >= '2017-01-01' AND date_from <= '2017-05-30')
AND username='Mike'
order by date_to ASC
此查询给出以下结果:
room_name date_from date_to
A1 2017-02-01 2017-02-10
A3 2017-02-16 2017-02-20
A1 2017-03-01 2017-03-18
有没有办法获得其余(相反)的日期范围?我的意思是我想知道 Mike 没有住在一个房间里的范围。对于具体示例,我想获得的结果将是以下日期范围:
2017-01-01 to 2017-01-31
2017-02-11 to 2017-02-15
2017-02-21 to 2017-02-28
2017-03-19 to 2017-05-30
非常感谢您的帮助!!!
创建房间 table,如上所述:
CREATE TABLE rooms (
id_user_room INTEGER,
username CHAR(10),
room_name CHAR(10),
date_from TIMESTAMP,
date_to TIMESTAMP
);
INSERT INTO rooms (id_user_room, username, room_name, date_from, date_to) VALUES
(1, 'mike', 'A1', '2017-02-01', '2017-02-10'),
(2, 'evans', 'A2', '2017-01-20', '2017-01-30'),
(3, 'mike', 'A3', '2017-02-16', '2017-02-20'),
(4, 'mike', 'A1', '2017-03-01', '2017-03-18')
;
您必须创建一个辅助日期 table,类似于:
CREATE TABLE dates (
day TIMESTAMP
);
INSERT INTO dates (day) VALUES
('2017-01-01'),
('2017-01-02'),
('2017-01-03'),
...
('2017-12-29'),
('2017-12-30'),
('2017-12-31')
;
用这个你可以找到迈克不在房间里的日期:
CREATE TABLE empty_dates AS
SELECT
d.day
FROM
dates d
LEFT JOIN
rooms r ON r.username = 'mike' AND d.day BETWEEN r.date_from AND r.date_to
WHERE
r.id_user_room IS NULL
;
为了简洁起见,我具体化了这个查询而不是使用子查询。在其他 SQL 语言中,如 TSQL 或 PostgreSQL 我宁愿使用 CTE。现在您可以通过将此 table 与其自身连接来指定间隔的开始和结束日期。
SELECT
CASE WHEN ed3.day IS NULL THEN 'Start date' ELSE 'End date' END row_type,
ed1.day
FROM
empty_dates ed1
LEFT JOIN
empty_dates ed2 ON DATEDIFF(ed2.day, ed1.day) = 1
LEFT JOIN
empty_dates ed3 ON DATEDIFF(ed3.day, ed1.day) = -1
WHERE
-- the next date is in the rooms table
ed2.day IS NULL
-- the previous date is in the rooms table
OR ed3.day IS NULL
;
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(username VARCHAR(12) NOT NULL
,room_name CHAR(2) NOT NULL
,date_from DATE
,date_to DATE
,PRIMARY KEY (username,room_name,date_from)
);
INSERT INTO my_table VALUES
('mike','A1','2017-02-01','2017-02-10'),
('evans','A2','2017-01-20','2017-01-30'),
('mike','A3','2017-02-16','2017-02-20'),
('mike','A1','2017-03-01','2017-03-18');
SELECT username
, '2017-01-01' date_from
, MIN(date_from - INTERVAL 1 DAY) date_to
FROM my_table
GROUP
BY username
UNION
SELECT x.username
, x.date_to + INTERVAL 1 DAY
, MIN(y.date_from - INTERVAL 1 DAY)
FROM my_table x
JOIN my_table y ON y.date_to > x.date_to
AND y.username = x.username
GROUP
BY x.username
, x.date_to
UNION
SELECT username
, MAX(date_to + INTERVAL 1 DAY)
, '2017-05-30'
FROM my_table
GROUP
BY username;
+----------+------------+------------+
| username | date_from | date_to |
+----------+------------+------------+
| evans | 2017-01-01 | 2017-01-19 |
| mike | 2017-01-01 | 2017-01-31 |
| mike | 2017-02-11 | 2017-02-15 |
| mike | 2017-02-21 | 2017-02-28 |
| evans | 2017-01-31 | 2017-05-30 |
| mike | 2017-03-19 | 2017-05-30 |
+----------+------------+------------+
6 rows in set (0.01 sec)
这假设所有日期都在阈值内 - 但如果它们不在阈值内,这并不是一个特别复杂的调整。
我有以下mysqltable(房间)
id_user_room username room_name date_from date_to 1 mike A1 2017-02-01 2017-02-10 2 evans A2 2017-01-20 2017-01-30 3 mike A3 2017-02-16 2017-02-20 4 mike A1 2017-03-01 2017-03-18
如果我想知道 Mike 在 2017-01-01 和 2017-05-30 之间的房间,我执行以下 SQL 查询。
SELECT room_name, date_from, date_to
FROM rooms
WHERE (date_to >= '2017-01-01' AND date_from <= '2017-05-30')
AND username='Mike'
order by date_to ASC
此查询给出以下结果:
room_name date_from date_to A1 2017-02-01 2017-02-10 A3 2017-02-16 2017-02-20 A1 2017-03-01 2017-03-18
有没有办法获得其余(相反)的日期范围?我的意思是我想知道 Mike 没有住在一个房间里的范围。对于具体示例,我想获得的结果将是以下日期范围:
2017-01-01 to 2017-01-31 2017-02-11 to 2017-02-15 2017-02-21 to 2017-02-28 2017-03-19 to 2017-05-30
非常感谢您的帮助!!!
创建房间 table,如上所述:
CREATE TABLE rooms (
id_user_room INTEGER,
username CHAR(10),
room_name CHAR(10),
date_from TIMESTAMP,
date_to TIMESTAMP
);
INSERT INTO rooms (id_user_room, username, room_name, date_from, date_to) VALUES
(1, 'mike', 'A1', '2017-02-01', '2017-02-10'),
(2, 'evans', 'A2', '2017-01-20', '2017-01-30'),
(3, 'mike', 'A3', '2017-02-16', '2017-02-20'),
(4, 'mike', 'A1', '2017-03-01', '2017-03-18')
;
您必须创建一个辅助日期 table,类似于:
CREATE TABLE dates (
day TIMESTAMP
);
INSERT INTO dates (day) VALUES
('2017-01-01'),
('2017-01-02'),
('2017-01-03'),
...
('2017-12-29'),
('2017-12-30'),
('2017-12-31')
;
用这个你可以找到迈克不在房间里的日期:
CREATE TABLE empty_dates AS
SELECT
d.day
FROM
dates d
LEFT JOIN
rooms r ON r.username = 'mike' AND d.day BETWEEN r.date_from AND r.date_to
WHERE
r.id_user_room IS NULL
;
为了简洁起见,我具体化了这个查询而不是使用子查询。在其他 SQL 语言中,如 TSQL 或 PostgreSQL 我宁愿使用 CTE。现在您可以通过将此 table 与其自身连接来指定间隔的开始和结束日期。
SELECT
CASE WHEN ed3.day IS NULL THEN 'Start date' ELSE 'End date' END row_type,
ed1.day
FROM
empty_dates ed1
LEFT JOIN
empty_dates ed2 ON DATEDIFF(ed2.day, ed1.day) = 1
LEFT JOIN
empty_dates ed3 ON DATEDIFF(ed3.day, ed1.day) = -1
WHERE
-- the next date is in the rooms table
ed2.day IS NULL
-- the previous date is in the rooms table
OR ed3.day IS NULL
;
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(username VARCHAR(12) NOT NULL
,room_name CHAR(2) NOT NULL
,date_from DATE
,date_to DATE
,PRIMARY KEY (username,room_name,date_from)
);
INSERT INTO my_table VALUES
('mike','A1','2017-02-01','2017-02-10'),
('evans','A2','2017-01-20','2017-01-30'),
('mike','A3','2017-02-16','2017-02-20'),
('mike','A1','2017-03-01','2017-03-18');
SELECT username
, '2017-01-01' date_from
, MIN(date_from - INTERVAL 1 DAY) date_to
FROM my_table
GROUP
BY username
UNION
SELECT x.username
, x.date_to + INTERVAL 1 DAY
, MIN(y.date_from - INTERVAL 1 DAY)
FROM my_table x
JOIN my_table y ON y.date_to > x.date_to
AND y.username = x.username
GROUP
BY x.username
, x.date_to
UNION
SELECT username
, MAX(date_to + INTERVAL 1 DAY)
, '2017-05-30'
FROM my_table
GROUP
BY username;
+----------+------------+------------+
| username | date_from | date_to |
+----------+------------+------------+
| evans | 2017-01-01 | 2017-01-19 |
| mike | 2017-01-01 | 2017-01-31 |
| mike | 2017-02-11 | 2017-02-15 |
| mike | 2017-02-21 | 2017-02-28 |
| evans | 2017-01-31 | 2017-05-30 |
| mike | 2017-03-19 | 2017-05-30 |
+----------+------------+------------+
6 rows in set (0.01 sec)
这假设所有日期都在阈值内 - 但如果它们不在阈值内,这并不是一个特别复杂的调整。