Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/281.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 如何在重叠的日期范围之间查找其余日期_Php_Mysql_Sql_Date_Overlap - Fatal编程技术网

Php 如何在重叠的日期范围之间查找其余日期

Php 如何在重叠的日期范围之间查找其余日期,php,mysql,sql,date,overlap,Php,Mysql,Sql,Date,Overlap,我有以下mysql表(房间) id\u用户\u房间用户名房间\u名称日期\u从日期\u到 1迈克A1 2017-02-01 2017-02-10 2埃文斯A2 2017-01-20 2017-01-30 3迈克A3 2017-02-16 2017-02-20 4迈克A1 2017-03-01 2017-03-18 如果我想知道Mike在2017-01-01和2017-05-30之间的房间,我将执行以下SQL查询 SELECT room_name, date_from, date_to FROM

我有以下mysql表(房间)

id\u用户\u房间用户名房间\u名称日期\u从日期\u到 1迈克A1 2017-02-01 2017-02-10 2埃文斯A2 2017-01-20 2017-01-30 3迈克A3 2017-02-16 2017-02-20 4迈克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
选择房间名称、日期从、日期到
从房间

其中(日期至>='2017-01-01'和日期自创建房间表,如上所述:

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')
;
您必须创建一个辅助日期表,类似于:

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')
;
有了这个,你可以找到Mike不住在房间里的日期:

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。现在您可以通过将此表连接到自身来指定间隔的开始和结束日期

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
;

这假设所有日期都在阈值范围内-但如果不在阈值范围内,这不是一个特别复杂的调整。

为什么不在PHP中执行此操作?如何使用PHP执行此操作,您有什么想法吗?创建一个包含一年中所有日期的表,然后进行左连接以查找日期,然后使用结果上的自连接查找结果中的不连续性。杂乱无章,但富有创意:)从所有日期范围中考虑第一个月和最后一个月,mike保留了上述日期范围(共3项记录)。他需要,那些约会范围,当他不留下的时候。我明白了吗?也许你想要的结果不是你真正需要的结果。你想要它做什么?草莓,非常感谢你的解决方案!它以一种高效的方式给了我想要的东西!!!!
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
;
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)