Сравнение двух диапазонов дат, когда один диапазон имеет диапазон дат начала - PullRequest
3 голосов
/ 22 апреля 2009

У меня возникла следующая проблема: Сравнение диапазонов дат

Решением для сравнения двух диапазонов является запрос:

ВЫБРАТЬ * С периодов ГДЕ НЕ (range_start> @check_period_end ИЛИ range_end <@check_period_start) </p>

У меня есть добавленная проблема. Я позволяю людям входить в ряд периодов. Чтобы быть точным, они вводят продолжительность (то есть 1 неделю) и диапазон дат начала (то есть первые 2 недели мая), и мне нужно выяснить, есть ли интервал в одну неделю в указанном диапазоне.

Наивным решением является запуск вышеуказанного запроса для каждого дня в моем диапазоне. Итак, для проверки 3-дневных интервалов за месяц мне нужно выполнить 30 запросов. Есть ли более эффективный способ?

Для бонусных баллов - я использую Django. Есть ли хорошее решение с использованием Django ORM?

Edit - Чтобы упростить вопрос, я думаю, что превратил его в другой вопрос! Моя актуальная проблема - найти «свободные пробелы». Я думаю, что это лишает законной силы некоторые из подходов «чистого SQL» ниже. Я думал, что было бы разумно начать новый вопрос , а не путать этот вопрос. Другие, скорее всего, сочтут этот вопрос полезным в его нынешнем виде.

Ответы [ 5 ]

1 голос
/ 23 апреля 2009

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

Это позволило сэкономить более 100 раз при выполнении чего-то похожего на скользящие средние в приложении, над которым я работал.

1 голос
/ 23 апреля 2009

Вы утверждаете, что пользователь указывает (в качестве примера):

  • 1 недельный период
  • дата начала (1 мая 2009 г.)
  • дата окончания (15 мая 2009 г.)


Затем вы заявляете, что вам нужно " выяснить, есть ли интервал в одну неделю в указанном диапазоне ". Я не уверен на 100%, что если я правильно понимаю, но это то, что я получаю от этого ...

  • Существует таблица «доступных периодов» (описывается датами начала / окончания)
  • Вам нужно найти «период ожидания», который за кругом с датами начала / окончания пользователя
  • Это перекрытие должно длиться не менее 1 недели (или любой другой продолжительности, необходимой пользователю)


Если бы это было так, я бы разработал это следующим образом ...

  • Укажите периоды, которые перекрываются
  • Укажите дату первого перекрытия
  • Определите дату последнего перекрытия
  • Если эти даты разнесены на 7 дней, это совпадение


Мое решение в SQL будет ...

SELECT
   *
FROM
   periods
WHERE 
   (range_start <= @check_end)
   AND (range_end >= @check_start)
   AND DATEDIFF(
          DAY,
          CASE WHEN range_start > @check_start THEN range_start ELSE @check_start END,
          CASE WHEN range_end   < @check_end   THEN range_end   ELSE @check_end   END
          )
       >= @required_duration-1


EDIT

Предполагается, что даты начала и окончания являются включительными, как подразумевается в логике вашего примера.
(Однодневный период, представленный «2009 01 января» -> «2009 01 января»)

Я лично предпочитаю дату начала Inclusive, дата окончания Exclusive.
(Однодневный период, представленный «2009 01 января» -> «2009 янв 02»)

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

  • При работе на почасовом уровне «2009 янв. 01» -> «2009 янв. 01» означает один час.
  • Но '2009 Jan 01' -> '2009 Jan 02' - это всегда день, если вы знаете, что дата окончания - Эксклюзив.
1 голос
/ 22 апреля 2009

Проблема проще, чем может показаться на первый взгляд, так как пользователь непосредственно не указывает дату окончания в своих критериях.

SELECT * FROM periods p
WHERE p.range_start >= @min_start
AND   p.range_start <= @max_start
AND   DATE_ADD(p.range_start, INTERVAL @duration DAY) <= p.range_end
1 голос
/ 22 апреля 2009

Это не хороший кандидат для SQL.

Однако в Django вы освобождены от многих ограничений SQL.

Сначала определите функцию метода в вашей модели, которая делает то, что вы хотите - в Python.

Например

class MyThing( models.Model ):
    startDate = models.DateField(...)
    duration = models.IntegerField(...)
    def isInside( self, aDate, aDuration ):
        return aDate >= self.startDate and aDate+aDuration <= self.startDate+self.duration

Затем используйте ваш метод isInside() для определения объектов. Это сделает часть работы в Python, где это намного проще, чем копаться в SQL.

Определите пользовательский менеджер для сложных запросов, подобных этому. Вы собираетесь расширить базовый метод query_set, включив в него логику, подобную этой.

for thing in MyThing.objects.filter( startDate__gte=aDate, startDate__lte=aDate+duration ):
    if thing.isInside( aDate, duration ):
        return thing

При этом будет использоваться SQL для получения подмножества объектов с датами, которые должны включать искомый интервал. Затем вы можете выбрать интервал конечного объекта из этого списка.

0 голосов
/ 22 апреля 2009

Как насчет этого?

Создайте таблицу дат, по одной строке на дату каледра.

  SELECT * FROM CalendarDates cd  
  LEFT JOIN period p 
      ON cd.caldate > p.end_date
      OR cd.caldate + duration < p.begin_date

  WHERE p.period_id IS NULL
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...