SQL 请求排除时间段

SQL request excluding periods of time

我需要获取所有 DISTINCT 用户,根据不可用时间段排除那些不可用的用户。

user table:

+------+-----------+--------------------------------------+
| id   | firstname | content                              |
+------+-----------+--------------------------------------+
| 13   | John      | ...                                  |
| 44   | Marc      | ...                                  |
| 55   | Elise     | ...                                  |
+------+-----------+--------------------------------------+

unavailability 期 table:

+------+-----------+--------------+--------------+
| id   | user_id   | start        | end          |
+------+-----------+--------------+--------------+
| 1    | 13        | 2019-07-01   | 2019-07-10   |
| 2    | 13        | 2019-07-20   | 2019-07-30   |
| 3    | 13        | 2019-09-01   | 2019-09-30   |
| 4    | 44        | 2019-08-01   | 2019-08-15   |
+------+-----------+--------------+--------------|

例如,我们希望用户在 2019-06-20 到 2019-07-05 之间有空:Marc 和 Elise 有空。

我必须使用 LEFT JOIN 吗?此请求无效:

SELECT DISTINCT user.*, unavailability.start, unavailability.end,
FROM user
LEFT JOIN unavailability ON unavailability.user_id = user.id
WHERE  
unavailability.start < "2019-06-20" AND unavailability.end > "2019-06-20"
AND unavailability.start < "2019-07-05" AND unavailability.end > "2019-07-05"

结果我需要:

+------+-----------+--------------------------------------+
| id   | firstname | content                              |
+------+-----------+--------------------------------------+
| 44   | Marc      | ...                                  |
| 55   | Elise     | ...                                  |
+------+-----------+--------------------------------------+

有了这个请求,我没有得到没有不可用时间段的 Elise。

DROP TABLE IF EXISTS user;

CREATE TABLE user

(id SERIAL PRIMARY KEY
,firstname VARCHAR(12) NOT NULL UNIQUE
);

INSERT INTO user VALUES
(13,'John'),
(44,'Marc'),
(55,'Elise');

DROP TABLE IF EXISTS unavailability ;

CREATE TABLE unavailability 
(id SERIAL PRIMARY KEY
,user_id INT NOT NULL
,start DATE NOT NULL
,end DATE NOT NULL
);

INSERT INTO unavailability VALUES
(1,13,'2019-07-01','2019-07-10'),
(2,13,'2019-07-20','2019-07-30'),
(3,13,'2019-09-01','2019-09-30'),
(4,44,'2019-08-01','2019-08-15');

SELECT x.* 
  FROM user x 
  LEFT 
  JOIN unavailability y 
    ON y.user_id = x.id 
   AND y.start <= '2019-07-05' 
   AND y.end >= '2019-06-20' 
 WHERE y.id IS NULL;
+----+-----------+
| id | firstname |
+----+-----------+
| 44 | Marc      |
| 55 | Elise     |
+----+-----------+
2 rows in set (0.01 sec)

可以使用这种方法:

select * from user k
where not exists (
select 1 from user 
join unavailability u on u.user_id = user.id
and ('2019-06-20' between start and end or '2019-07-05' between start and end)
where user.id = k.id)

您可以 select unavailables 的 ID,并在子查询中使用此结果:

架构 (MySQL v5.7)

CREATE TABLE user (
  `id` INTEGER,
  `firstname` VARCHAR(5),
  `content` VARCHAR(3)
);

INSERT INTO user
  (`id`, `firstname`, `content`)
VALUES
  (13, 'John', '...'),
  (44, 'Marc', '...'),
  (55, 'Elise', '...');



CREATE TABLE unavailability (
  `id` INTEGER,
  `user_id` INTEGER,
  `start` DATETIME,
  `end` DATETIME
);

INSERT INTO unavailability
  (`id`, `user_id`, `start`, `end`)
VALUES
  (1, 13, '2019-07-01', '2019-07-10'),
  (2, 13, '2019-07-20', '2019-07-30'),
  (3, 13, '2019-09-01', '2019-09-30'),
  (4, 44, '2019-08-01', '2019-08-15');

查询#1

SELECT *
FROM user us
WHERE us.id NOT IN (
  SELECT u.user_id
  FROM unavailability u
  WHERE u.start <= '2019-07-05' AND u.end >= '2019-06-20'
);

| id  | firstname | content |
| --- | --------- | ------- |
| 44  | Marc      | ...     |
| 55  | Elise     | ...     |

View on DB Fiddle


备注

这个条件:

unavailability.start < 2019-06-20 AND unavailability.end > 2019-06-20
AND unavailability.start < 2019-07-05 AND unavailability.end > 2019-07-05

会这样评价:

unavailability.start < 2019-06-20 AND unavailability.end > 2019-07-05

因为对于 unavailability.start < 2019-06-20 AND unavailability.start < 2019-07-05 部分,低于 2019-07-05 但高于 2019-06-20 的所有内容都将被排除(您正在使用 AND)。两者相同 unavailability.end