SQL-запрос без учета промежутков времени - PullRequest
1 голос
/ 07 июня 2019

Мне нужно получить всех пользователей DISTINCT, за исключением тех, которые недоступны в соответствии с периодами недоступности.

Таблица user:

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

Таблица периодов unavailability:

+------+-----------+--------------+--------------+
| 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 по 20-20 2019: 05: Марк и Элиз доступны.

Должен ли я использовать левое соединение? Этот запрос не работает:

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     | ...                                  |
+------+-----------+--------------------------------------+

С этим запросом я не получаю Элизу, у которой нет периодов недоступности.

Ответы [ 3 ]

2 голосов
/ 07 июня 2019

Этот подход можно использовать:

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)
2 голосов
/ 07 июня 2019
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)
1 голос
/ 07 июня 2019

Вы можете выбрать идентификаторы недоступных и использовать этот результат в подзапросе:

Схема (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     | ...     |

Просмотр на БД 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

...