Определить перекрывающиеся диапазоны дат из одной таблицы - PullRequest
35 голосов
/ 20 декабря 2010

У меня есть таблица со следующими данными

PKey  Start       End         Type
====  =====       ===         ====
01    01/01/2010  14/01/2010  S
02    15/01/2010  31/01/2010  S
03    05/01/2010  06/01/2010  A

И хочу получить следующие результаты

PKey  Start       End         Type
====  =====       ===         ====
01    01/01/2010  14/01/2010  S
03    05/01/2010  06/01/2010  A

Есть идеи, с чего начать? Многое из того, что я прочитал, говорит о том, что мне нужно создавать записи для каждого дня и присоединяться в соответствующие дни, это единственный способ?

Ответы [ 9 ]

36 голосов
/ 20 декабря 2010

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

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

select dr1.* from date_ranges dr1
inner join date_ranges dr2
on dr2.start > dr1.start -- start after dr1 is started
  and dr2.start < dr1.end -- start before dr1 is finished

.которые полностью находятся в другом интервале, или вам нужно объединить интервалы, то есть

PKey  Start       End         Type
====  =====       ===         ====
01    01/01/2010  20/01/2010  S
02    15/01/2010  31/01/2010  S

с получением

Start       End         Type
=====       ===         ====
01/01/2010  31/01/2010  S

, вам понадобятся более сложные вычисления.

По моему опытус такими проблемами, как только вы научитесь выполнять вычисления вручную, их легко перенести в SQL:)

4 голосов
/ 27 августа 2014

Когда мне нужно было сравнить два временных интервала в SQL для перекрытия, вот четыре сценария, о которых я мог подумать:

  1. Начало Span1 между началом Span2 и концом Span2
  2. Конец Span1 находится между началом Span2 и концом Span2
  3. Начало и конец Span1 находятся между началом Span2 и концом Span2
  4. Начало и конец Span2 находятся между началом Span1 и концом Span1

Вот оператор OR, который я создал для захвата этих сценариев (в моем случае Oracle SQL):

and (
    s1.start between s2.start and s2.end
    OR
    s1.end between s2.start and s2.end
    OR
    s2.start between s1.start and s1.end
)
3 голосов
/ 20 декабря 2010

Возможно:

SELECT A.PKey, A.Start, A.End, A.Type
FROM calendar AS A, calendar AS B
WHERE (p.pkey<>a.pkey
AND b.start>=a.start
AND b.end<=a.end)
OR (b.pkey<>a.pkey
AND b.start<=a.start
AND b.end>=a.end)
2 голосов
/ 20 декабря 2010
select A.*
from MyTable A
inner join MyTable B
on (B.start <= A.end)
and (B.end >= A.start)

или что-то в этом роде (при условии, что даты не обнуляются и равные даты считаются перекрытием).

1 голос
/ 28 февраля 2013

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

В моем приложении, например, у меня есть политики с одинаковым номером политики, но, возможно, описание политики меняется с одного финансового года на следующий. Когда пользователь вводит новую запись (тот же номер политики, другое описание политики), мне нужен был способ узнать, существует ли эта политика для указанного временного диапазона. Если новые даты вступления в силу / истечения срока действия политики перекрываются с тем, что уже есть в базе данных, мне нужно было сделать ошибку и сообщить пользователю, почему их ввод был неверным.

Для этого я использовал следующее предикатное утверждение:

AND @_expiration >= EffectiveDate AND ExpirationDate >= @_effective

Надеюсь, кто-то найдет это таким же полезным, как и я.

1 голос
/ 03 января 2013

Мне пришлось сделать очень похожую вещь, чтобы остановить ввод повторяющихся выходных в таблицу. он был в доступе и записан в temptable на входе, поэтому пришлось запросить его в VBA SQL:

 stCommandText = "SELECT " _
                    & "* " _
                    & "FROM " _
                    & "TableName a, " _
                    & "TableName b " _
                    & "WHERE " _
                    & "a.ID = b.ID " _
                    & "AND a.Startdate >= b.Startdate AND a.StartDate <= b.EndDate " _
                    & "AND a.AutoNo <> b.AutoNo "
0 голосов
/ 07 июля 2016

Sql = 'SELECT task_id, task_start_date, task_due_date ОТ (wba_task) ГДЕ (task_start_date <= "2016-07-13" И <code>task_due_date> = "2016-07-25") ИЛИ (task_due_date МЕЖДУ" 2016-07-13 "и" 2016-07-25 ") ';

Запрос Codeigniter приведен ниже.

$fromdaysDate="2016-07-13";//changed date
$todaysDate="2016-07-25";//changed date
$this->db->select('task_id,task_start_date, task_due_date'); 
$this->db->where('task_start_date <="'.date('Y-m-d', strtotime($fromdaysDate)).'"');
$this->db->where('task_due_date >="'.date('Y-m-d', strtotime($todaysDate)).'"');    
$this->db->or_where('task_due_date BETWEEN "'. date('Y-m-d', strtotime($fromdaysDate)). '" and "'. date('Y-m-d', strtotime($todaysDate)).'"');   
$alltask=$this->db->get('wba_task')->result_array();
echo $this->db->last_query();

получить всю базу данных данных перекрытия .... enter image description here

0 голосов
/ 14 апреля 2014

КСТАТИ - Если у вас нет уникального идентификатора, против ваших дат вы можете сделать это oracle..FYI

with date_ranges
as
(
SELECT 
     rownum as pkey,
    date_ranges.*
FROM  date_ranges
) 
select 
dr1.* 
from 
date_ranges dr1 , date_ranges dr2
where  dr1.pkey > dr2.pkey
AND dr1.end_dt >= dr2.start_dt 
AND dr2.end_dt >= dr1.start_dt
0 голосов
/ 20 июня 2013

В MySQL вам в основном необходимо:

SELECT COUNT(*) FROM date_ranges AS A, date_ranges AS B WHERE A.id <> B.id AND A.id > B.id AND A.end_at > B.start_at AND B.end_at > A.start_at

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

Эта тема относится к "Интервальной алгебре Аллена", и по этой ссылке можно прочесть еще несколько ссылок:

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