Как мне сравнить перекрывающиеся значения в строке? - PullRequest
3 голосов
/ 30 декабря 2008

Кажется, у меня проблема с этим запросом SQL:

SELECT * FROM appts 
WHERE timeStart >='$timeStart' 
AND timeEnd <='$timeEnd' 
AND dayappt='$boatdate'

Время отформатировано как военное время. Суть в том, что аренду лодок можно забронировать с 7:00 до 13:00 или с 9:00 до 13:00 или с 9:00 до 17:00. Если в этом диапазоне есть appt, он должен вернуть appts, но он оказался несовместимым. Если я выберу 9 утра до 1 вечера, он будет игнорировать приложения, которые начались с 7 утра, даже если он перекрывает 9 утра до 1 вечера. Если я выберу с 9 до 5, он ничего не вернет, хотя с 7 утра до 1 вечера. Как сделать оператор SQL, включающий весь диапазон от timeStart до timeEnd, включая перекрывающиеся?

Ответы [ 4 ]

8 голосов
/ 30 декабря 2008

Шахкалпеш ответил на вопрос:

Я думаю, тебе нужно ИЛИ.

SELECT * FROM appts 
WHERE (timeStart >='$timeStart' 
OR timeEnd <='$timeEnd')
AND dayappt='$boatdate'

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

Это совершенно неправильно - @ShaneD верен. Например, при этом будет выбрано бронирование между 05:00 и 06:00, поскольку фактическое время окончания меньше, чем любое другое время, о котором вы спрашиваете. Он также будет получать аренду с 18:00 по аналогичной причине.

В ответ на мой комментарий Шахкальпеш попросил:

Не могли бы вы опубликовать отдельный ответ с данными и входными параметрами с ожидаемым выводом?

Достаточно справедливо - да. Слегка отредактировано, вопрос говорит:

Логика в том, что прокат лодок можно забронировать

  • с 7:00 до 13:00 или
  • с 9:00 до 13:00 или
  • с 9:00 до 17:00.

Если в этом диапазоне назначена встреча, она должна возвращать встречи, но она оказалась непоследовательной. Если я выберу 9 утра до 1 вечера, ...


Достаточно фона. Мы можем игнорировать дату назначений и просто учитывать время. Я предполагаю, что есть простой способ ограничить время записи в формате чч: мм; на самом деле не все СУБД предоставляют это, но расширение для обработки hh: mm: ss тривиально.

Appointments

Row     timeStart   timeEnd   Note
  1     07:00       13:00     First valid range
  2     09:00       13:00     Second valid range
  3     09:00       17:00     Third valid range
  4     14:00       17:00     First plausibly valid range
  5     05:00       06:00     First probably invalid range
  6     18:00       22:30     Second probably invalid range

При поиске встреч, перекрывающих диапазон 09:00 - 13:00, запрос Шахкальпеша (упрощенный) становится:

SELECT * FROM Appointments
    WHERE (timeStart >= '09:00' OR timeEnd <= '13:00')

Это вернет все шесть строк данных. Однако только строки 1, 2, 3 перекрывают период времени с 09:00 до 13:00. Если строки 1, 2 и 3 являются единственными действительными репрезентативными значениями встреч, тогда запрос Шахкальпеша дает правильный ответ. Однако, если строка 4 (которую я считаю правдоподобно действительной) разрешена, ее не следует возвращать. Аналогично, строки 5 и 6 - если они есть - не должны возвращаться. [ На самом деле, предполагая timeStart <= timeEnd для всех строк в таблице (и нет никаких значений NULL, чтобы что-то испортить), мы можем видеть, что запрос Шахкалпеша возвратит ЛЮБУЮ строку данных для 09 : 00-13: 00, потому что либо время начала строки больше 09:00, либо время окончания меньше 13:00, либо оба значения. Это равносильно написанию 1 = 1 или любой другой тавтологии в предложении WHERE. ]

Если мы рассмотрим запрос ShaneD (как упрощенный):

SELECT * FROM Appointments
    WHERE timeStart <= '13:00' AND timeEnd >= '09:00'

мы видим, что он также выбирает строки 1, 2 и 3, но отклоняет строки 4 (потому что timeStart> '13: 00 '), 5 (потому что timeEnd <'09: 00') и 6 (потому что timeStart> '13: 00' ). Это выражение является архетипическим примером того, как выбирать перекрывающиеся строки, считая «встречаются» и «встречаются» (см., Например, « Алгебра интервалов Аллена »). Изменение «> =» и «<=» изменяет набор интервалов, считающихся перекрывающимися. </p>

6 голосов
/ 30 декабря 2008

Правильная проверка будет выглядеть так:

SELECT * FROM appts 
WHERE timeStart <='$timeEnd' 
AND timeEnd >='$timeStart' 
AND dayappt='$boatdate'

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

Скажем, период времени, который я проверяю, сегодня, я хочу найти любой период времени, который не перекрывается сегодня. Для этого есть только два сценария: период времени, начинающийся после сегодняшнего дня (PeriodStart> EndOfToday), или период времени, заканчивающийся до сегодняшнего дня (PeriodEnd

Учитывая, что у нас есть простой тест на отсутствие совпадений:
(PeriodStart> EndOfToday) ИЛИ (PeriodEnd

Быстрый переворот, и у вас есть простой тест на перекрытие:
(PeriodStart <= EndOfToday) И (PeriodEnd> = StartOfToday)

-Shane

0 голосов
/ 31 декабря 2008

Спасибо, Шейн, Шахкальпеш и Джонатан.

Я фактически упустил из виду тот факт, что Шейн «поменял местами» переменные (я все еще использовал timeStart <= $ timeStart, когда это должно быть timeStart <= $ timeEnd). Я побежал с измененным утверждением, как предложил Джонатан / Шейн, и это работает. Как отметил Джонатан, я, очевидно, пропустил некоторые временные диапазоны, с которыми я должен был протестировать. </p>

Теперь с объяснениями Джонатана, я теперь получаю более полное представление о моей ошибке, и это полезно.

0 голосов
/ 30 декабря 2008

Я думаю, вам нужно ИЛИ.


SELECT * FROM appts 
WHERE (timeStart >='$timeStart' 
OR timeEnd &LT;='$timeEnd')
AND dayappt='$boatdate'

Предполагая, что каждая запись касается только определенного дня. то есть арендованные лодки не пробегают более 1 дня.

...