Запрос на поиск ближайшего свободного временного интервала в mysql - почему он не работает?

0

У меня есть таблица под названием notes, и там у меня есть три поля:

id  |      start_time       |  duration
 1  |  2015-10-21 19:41:35  |    15
 2  |  2015-10-21 19:41:50  |    15
 3  |  2015-10-21 19:42:05  |    15
 4  |  2015-10-21 19:42:35  |    15
etc.

id - поле INT

start_time - поле TIMESTAMP

duration - это поле INT, в котором указано количество секунд, сколько времени занимает каждое событие.

Я пишу SQL-запрос, который будет получать 3 поля в качестве входных данных: duration, begin_time и end_time и вернет мне поле timestamp, в котором может быть только что новое.

Основываясь на множестве вопросов, похожих на мои, на StackOverflow (в основном, этот MySQL/PHP - найти доступные временные интервалы) Я создал запрос:

SELECT (a.start_time + a.duration) AS free_after FROM notes a
WHERE NOT EXISTS ( SELECT 1 FROM notes b
WHERE b.start_time BETWEEN (a.start_time+a.duration) AND
(a.start_time+a.duration) + INTERVAL '$duration' SECOND) AND 
(a.start_time+a.duration) BETWEEN '$begin_time' AND '$end_time'

Но когда я запускаю его следующим образом:

SELECT (a.start_time + a.duration) AS free_after FROM notes a
WHERE NOT EXISTS ( SELECT 1 FROM notes b WHERE b.start_time
BETWEEN (a.start_time+a.duration) AND (a.start_time+a.duration) + INTERVAL 15 SECOND )
AND (a.start_time+a.duration) BETWEEN '2015-11-21 19:41:30' AND '2015-11-21 19:43:50'

Я не получаю никаких записей, хотя, глядя на таблицу, упомянутую выше, я должен получить результат:

     free_after 
2015-11-21 19:42:20

(потому что между этими двумя записями:

 3  |  2015-10-21 19:42:05  |    15
 4  |  2015-10-21 19:42:35  |    15

имеется свободный слот продолжительностью 15 секунд). Итак, почему мой запрос не работает должным образом?

==== EDIT:

После рекомендации Ричарда я сделал запрос show warnings, и он вернул мне значения:

Truncated incorrect DOUBLE value: '2015-11-21 19:4...
Truncated incorrect DOUBLE value: '2015-11-21 19:4...
Truncated incorrect DOUBLE value: '2015-11-21 19:4...
Truncated incorrect DOUBLE value: '2015-11-21 19:4...
Incorrect datetime value: '0'

и я не уверен, что вызывает его, не могли бы вы мне помочь?

===== РЕДАКТИРОВАТЬ 2

Хорошо, после следующего совета @Richard (спасибо btw!), я изменил свой запрос на следующий:

SELECT (a.start_time + INTERVAL a.duration SECOND) AS free_after FROM notes a
WHERE
NOT EXISTS ( SELECT 1 FROM notes b WHERE b.start_time
BETWEEN (a.start_time + INTERVAL a.duration SECOND) AND
(a.start_time + INTERVAL a.duration SECOND) + INTERVAL 15 SECOND ) AND
(a.start_time + INTERVAL a.duration SECOND) BETWEEN '2015-11-21 19:41:30' AND '2015-11-21 19:43:50'

и на этот раз я получил результат (что является хорошим сообщением):

2015-10-21 19:42:50

но это плохое значение (это худшее сообщение). Это должно быть 2015-10-21 19:42:20... Есть ли что-нибудь еще, что я делаю неправильно здесь? Когда я делаю show warnings сразу после него - здесь больше ничего нет: (

  • 0
    После попытки запроса на выборку он должен сообщить 8 предупреждений - если вы затем show warnings; в нем перечислены проблемы с вашим запросом.
  • 0
    Спасибо @Richard за ваше предложение, я написал предупреждения на шоу, и теперь я точно вижу, что происходит, но я не знаю, в чем может быть точная проблема. Я подозреваю, что проблема в том, что поле start_time является меткой времени, и мне, вероятно, придется изменить свой запрос для работы с метками времени, а не с датой, но я не знаю точно, как это исправить, не могли бы вы помочь мне с этим? Пожалуйста, проверьте мои правки на оригинальный вопрос, спасибо!
Показать ещё 4 комментария
Теги:

4 ответа

2
Лучший ответ

Предложение BETWEEN точно соответствует датам окончания. С вашими примерными данными, как только вы добавите длительность 15 секунд и фиксированную продолжительность, также равную 15 секундам, она будет соответствовать вашим последним данным образца, которые 30 секунд, и, следовательно, исключает. Измените свой запрос на

SELECT (a.start_time + INTERVAL a.duration SECOND) AS free_after FROM notes a
WHERE
NOT EXISTS ( SELECT 1 FROM notes b WHERE b.start_time
BETWEEN (a.start_time + INTERVAL a.duration SECOND) AND
(a.start_time + INTERVAL a.duration SECOND) + INTERVAL 15 SECOND - INTERVAL 1 MICROSECOND) AND
(a.start_time + INTERVAL a.duration SECOND) BETWEEN '2015-10-21 19:41:30' AND '2015-10-21 19:43:50'

(т.е. снятие 1 микросекунды в качестве регулировки)

и вы получите ответ:

+---------------------+
| free_after          |
+---------------------+
| 2015-10-21 19:42:20 |
| 2015-10-21 19:42:50 |
+---------------------+
2 rows in set (0.00 sec)
  • 0
    Спасибо большое за помощь!
  • 0
    Ричард ты чуду!
1

Короткий ответ: потому что вы не можете просто добавить TIMESTAMP в INT.

См. например: Как добавить смещение во все временные метки /DATETIME в базе данных MySQL?

Длинный ответ:

Как правило, при отладке сложного SQL-запроса начинайте с более простой формы, а затем добавляйте свои более сложные предложения один за другим, пока не прекратите делать то, что ожидаете, а затем исправьте это. В вашем случае начните с

SELECT (a.start_time + a.duration) AS free_after FROM notes a;

Что, когда я пытаюсь это сделать в своей системе, дает:

+----------------+
| free_after     |
+----------------+
| 20151021194150 |
| 20151021194165 |
| 20151021194220 |
| 20151021190250 |
+----------------+

Для меня это не похоже на обычные значения DATETIME или TIMESTAMP. Я заметил, что в одном месте ваш запрос использует нотацию INTERVAL <value> SECOND для добавления смещения к TIMESTAMP, но это игнорируется во всех других дополнениях.

mysql> SELECT (a.start_time + INTERVAL a.duration SECOND) AS free_after FROM notes a;
+---------------------+
| free_after          |
+---------------------+
| 2015-10-21 19:41:50 |
| 2015-10-21 19:42:05 |
| 2015-10-21 19:42:20 |
| 2015-10-21 19:02:50 |
+---------------------+
4 rows in set (0.00 sec)

Это выглядит лучше. Следуя тому же принципу, полный запрос дает ожидаемый результат.

Кроме того, как заметил Ричард, вы должны были видеть предупреждения. Использование show warnings; в mysql тогда дало бы вам подобный ключ, жалуясь на "Неверное значение даты и времени" и "Усеченное неправильное значение DOUBLE".

  • 0
    Большое спасибо, я последовал вашему совету и совету Ричарда и пошел немного дальше, но в моем запросе все еще есть какая-то ошибка, я отредактировал вопрос во второй раз, не могли бы вы взглянуть на него и сказать, что может быть там не так?
  • 0
    @ user3766930 Опять же, ваши запросы и данные, похоже, перепутаны относительно того, должна ли дата быть в октябре или ноябре. С данными, которые вы процитировали изначально, и вашим запросом Edit 2, настроенным на использование Октябрь, я получаю 2015-10-21 19:42:20 , так что, должно быть, что-то другое на вашей стороне?
Показать ещё 1 комментарий
0

Вы не можете добавить int к дате. Попробуйте следующее:

    SELECT notes.starttime+ INTERVAL notes.duration SECOND
FROM notes
WHERE notes.starttime+INTERVAL notes.duration SECOND<= '2015-11-21 19:41:30'
  AND NOT EXISTS (SELECT notes.id 
      FROM notes 
      WHERE notes.starttime <= '2015-11-21 19:43:50'
        AND notes.starttime+ INTERVAL notes.duration SECOND>='2015-11-21 19:43:50')
ORDER BY notes.starttime
0

Вы можете имитировать функцию LAG() и...:

  • Во-первых. Рассчитайте разницу между текущей строкой и последней.
  • Во-вторых. Выберите строки с разницей больше, чем продолжительность

Попробуйте этот SQL:

SET @lag_time='0000-00-00 00:00:00';
SELECT TIMESTAMPADD( SECOND, duration, lastTime ) as fromTime,
       (diffTime - duration) freeSeconds,
       currTime toTime
FROM
  (SELECT @lag_time as LastTime, 
         IFNULL(TIMESTAMPDIFF(SECOND, @lag_time, start_time ),0) diffTime,
         @lag_time:=start_time currTime,
         duration       
  FROM notes) as TimeDiff
WHERE diffTime > duration
ORDER BY currTime;

Ещё вопросы

Сообщество Overcoder
Наверх
Меню