Подтвердить что ты не робот

Найти интервал соответствия в SQL - Oracle

У меня есть таблица:

Таблица:

start         end
1 Jan 09    31 Jan 2009
1 Feb 09    28 Feb 2009
1 Mar 09    31 Mar 2009
1 Apr 09    01 May 2009
1 May 09    31 May 2009
1 Jun 09    01 Jul 2009
1 Jul 09    31 Jul 2009
1 Aug 09    31 Aug 2009
1 Sep 09    01 Oct 2009
1 Oct 09    31 Oct 2009
1 Nov 09    01 Dec 2009
1 Dec 09    31 Dec 2009
1 Jan 10    31 Jan 2010
1 Feb 10    03 Mar 2010
1 Mar 10    31 Mar 2010
1 Apr 10    01 May 2010
1 May 10    31 May 2010
1 Jun 10    01 Jul 2010
1 Jul 10    31 Jul 2010
1 Aug 10    31 Aug 2010
1 Sep 10    01 Oct 2010
1 Oct 10    31 Oct 2010
1 Nov 10    01 Dec 2010
1 Dec 10    31 Dec 2010
1 Jan 09    31 Mar 2009
1 Apr 09    30 Jun 2009
1 Jul 09    01 Oct 2009
1 Oct 09    31 Dec 2009
1 Jan 10    31 Mar 2010
1 Apr 10    30 Jun 2010
1 Jul 10    01 Oct 2010
1 Oct 10    31 Dec 2010
1 Jan 09    31 Dec 2009
1 Jan 10    31 Dec 2010

Вышеуказанное содержит для каждого месяца, квартала и года 2009, 2010.

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

таблица2

start       end
15-12-09    31-12-09
15-01-12    31-12-13
01-01-11    31-12-13
30-01-98    31-12-13
01-01-98    31-12-13
01-01-98    31-12-13
23-12-12    31-12-13
12-11-11    31-12-13
01-01-10    31-12-13

Для каждой записи в таблице 2 мне нужно найти возможные таймфреймы, которые она попадает в таблицу1.

Например, из таблицы2, первая запись -

15-12-09    31-12-09

падает до:

1 Dec 09    31 Dec 2009
1 Oct 09    31 Dec 2009
1 Jan 09    31 Dec 2009

Возможно ли в Oracle SQL идентифицировать его?

4b9b3361

Ответ 1

Сначала вы должны определить, что вы имеете в виду с падением в интервале table1

Существуют две общие интерпретации. Более ограничительным является SUBINTERVAL, т.е. согласованный интервал полностью покрывается опорным интервалом.

 match              <----------> 
 reference    <------------------>

Другая более расслабленная возможность INTERSECT, что означает, что для обоих интервалов есть как минимум одна точка.

 match                       <----------> 
 reference   <------------------>

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

Обратите внимание, что таблицы с вашими имитируемыми данными создаются ниже.

select 
   tab2.start_d match_start, tab2.end_d match_end, 
   tab.start_d ref_start, tab.end_d ref_end 
from tab2 
join tab
-- option SUBINTERVAL
on tab.start_d <= tab2.start_d and tab2.end_d <= tab.end_d
-- option INTERSEC
--  on NOT (tab2.end_d <  tab.start_d OR tab2.start_d > tab.end_d)
order by 1,2,3;

для опции SUBINTERVAL

MATCH_START       MATCH_END         REF_START         REF_END         
----------------- ----------------- ----------------- -----------------
15.12.09 00:00:00 31.12.09 00:00:00 01.01.09 00:00:00 31.12.09 00:00:00 
15.12.09 00:00:00 31.12.09 00:00:00 01.10.09 00:00:00 31.12.09 00:00:00 
15.12.09 00:00:00 31.12.09 00:00:00 01.12.09 00:00:00 31.12.09 00:00:00  

Вы получите гораздо больше записей для опции INTERSECT.

И здесь тестовые данные

create table tab as
with tab as (
-- reference intervals
-- months
select add_months(to_date('01012009','ddmmyyyy'),rownum-1) start_d,
add_months(to_date('01012009','ddmmyyyy'),rownum)-1 end_d from dual connect by level <=24
union all
-- quartals
select add_months(to_date('01012009','ddmmyyyy'),3*(rownum-1)) start_d,
add_months(to_date('01012009','ddmmyyyy'),3*rownum)-1 end_d from dual connect by level <=24/3
union all
-- years
select add_months(to_date('01012009','ddmmyyyy'),12*(rownum-1)) start_d,
add_months(to_date('01012009','ddmmyyyy'),12*rownum)-1 end_d from dual connect by level <=24/12
) 
select * from tab;

create table tab2 as
with tab2 as (
-- matched intervals
select to_date('15-12-09','dd-mm-rr') start_d,     to_date('31-12-09','dd-mm-rr') end_d from dual union all
select to_date('15-01-12','dd-mm-rr') start_d,     to_date('31-12-13','dd-mm-rr') end_d from dual union all
select to_date('15-01-98','dd-mm-rr') start_d,     to_date('31-12-13','dd-mm-rr') end_d from dual
)
select * from tab2;

Ответ 2

Простой вариант должен помочь.

Сначала используйте два оператора, чтобы проверить, является ли start_date таблицы2 внутри таймфрейма таблицы1, а второй между будет проверять, является ли end_date таблицы2 также внутри того же таймфрейма таблицы1. Обе проверки должны выполняться одновременно, поэтому вы должны использовать их и между ними.

Кроме того, включение инклюзивного "между" работает следующим образом: для первого параметра он проверяет > или = и для второго параметра < или =.

Таким образом, следующие утверждения равны:

t2.start_date between t1.start_date and t1.end_date

и

t2.start_date >= t1.start_date and t2.start_date <= t1.end_date

SQL, который вы ищете, будет выглядеть следующим образом:

select t2.*, '->', t1.* from table2 t2,
  table1 t1
  where t2.start_date between t1.start_date and t1.end_date
    and t2.end_date between t1.start_date and t1.end_date

Ответ 3

Во-первых, жизнь будет намного проще, если вы сохраните все в стандартном формате DATE, чтобы вы не конвертировали в запросах. Тогда шаблон просто:

WHERE table2.start <= table1.end
  AND table1.start <= table2.end

Ответ 4

В SQL лучший способ выразить пересечение найденных интервалов:

select *
from table2 
join table1 
on
   table1.start between table2.start and table2.end
   or 
   table2.start between table1.start and table1.end;

Каждый случай пересечения падает в одном из этих двух условий.

Ответ 5

Я выбрал не очень простой способ без большой производительности =)

with days (dt, max_dt) as
  (select (select min(start_dt) from table2) as dt
         ,(select max(end_dt) from table2) as max_dt
   from dual
   union all
   select dt+1 as dt
         ,max_dt as max_dt
   from days
   where dt <max_dt
  )
select distinct
       t2.start_dt as start_dt2
      ,t2.end_dt as end_dt2
      ,t.start_dt as start_dt
      ,t.end_dt as end_dt
from table2 t2
join days d on (t2.start_dt <=d.dt and t2.end_dt >= d.dt)
join table t on (t.start_dt <=d.dt and t.end_dt >= d.dt)

Я пытаюсь осветить случаи ниже:

table interval        |---------|
table2 interval    |---------------|
table2 interval         |----|
table2 interval             |--------|
table2 interval    |-----|

Ответ 6

Я не знаю, что вы имеете в виду, и чего вы хотели достичь здесь, но я надеюсь, что это поможет вам. Так или иначе, я смог получить вывод, на который вы ссылались (частично). Просто немного подкорректируйте этот код.

SELECT  CONVERT(VARCHAR(25),CONVERT(DATE,tbl1.START)) 'start',
    CONVERT(VARCHAR(25),CONVERT(DATE,tbl1.[END])) 'end',
    CONVERT(VARCHAR(25),CONVERT(DATE,tbl2.START)) + ' - ' + CONVERT(VARCHAR(25),CONVERT(DATE,tbl2.[END])) 'table2'

FROM tbl1, tbl2 где (CONVERT (DATE, tbl2.START) >= CONVERT (DATE, tbl1.START)) И       (CONVERT (DATE, tbl2. [END]) <= CONVERT (DATE, tbl1. [END]))