2015-12-09 17 views
10

Ho una tabella:Trova intervallo corrispondente in SQL - Oracle

tavolo:

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 

È possibile che questo contiene per ogni mese, trimestre e l'anno 2009, 2010.

Ho un altro tabella con il seguente:

table2

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 

Per ogni voce in tabella2, devo trovare i possibili tempi in cui cade in table1.

Per es. da table2, prima voce -

15-12-09 31-12-09 

cade a:

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

E 'possibile in Oracle SQL per identificarlo?

+1

Se possibile, è possibile fornire script di inserimento? – Buddi

+0

Restituisce i record in cui la data di fine in una tabella è successiva o uguale alla data di inizio nell'altra tabella E la data di inizio nella tabella è precedente o uguale alla data di fine dell'altra tabella. – Rabbit

+0

La risposta di Marmite Bomber fornisce risposte corrette per entrambi gli scenari ... – dnoeth

risposta

2

È necessario prima definire che cosa si intende con cadute in table1 intervallo

Ci sono due generale possibile interpretazione. Un più restrittivo è il SUBINTERVAL , , ovvero l'intervallo corrispondente è completamente coperto dall'intervallo di riferimento.

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

Altra possibilità è più rilassato INTERSECT che significa che v'è almeno un punto comune ai due intervalli.

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

In base a tale decisione si utilizza una condizione di join diversa. Nella query seguente viene implementata la prima possibilità, scambiare semplicemente i commenti per ottenere l'altra opzione.

Si noti che le tabelle con i dati simulati vengono create di seguito.

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; 

risultato per l'opzione sottointervallo

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 

Otterrete molto di più record per l'opzione si intersecano.

E qui i dati di test

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; 
+0

fallisce se cambio 15-01-12 a 15-01-98 in tab2. – dang

+0

@dang cosa fallisce? L'intervallo da 15-01-98 a 31-12-13 non è incluso in nessun intervallo di 'tab', poiché la query restituisce valori NULL nelle colonne START e END. Ho aggiunto questa nuova riga a 'tab2' –

+0

Se cambio select to_date ('15 -01-12 ',' dd-mm-rr ') start_d, to_date ('31 -12-13', 'dd-mm-rr ') end_d da dual TO seleziona to_date ('15 -01-98', 'dd-mm-rr') start_d, to_date ('31 -12-13 ',' dd-mm-rr ') end_d da dual, it mi dà NULL – dang

1

In primo luogo, la vita sarà molto più semplice se si memorizza tutto in formato data standard in modo che non si sta convertendo nelle query. Quindi il modello è semplicemente:

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

Semplice tra dovrebbe aiutare.

Utilizzare prima due istruzioni tra parentesi per verificare se start_date di table2 all'interno del timeframe di table1, il secondo tra controllerà se end_date di table2 anche all'interno dello stesso timeframe di table1.Entrambi gli assegni devono essere conservati contemporaneamente, quindi è necessario utilizzarli e tra di essi.

Inoltre, essendo una società inclusiva "tra" opere seguente modo: per il primo parametro controlla se > o = e per il secondo parametro < o =.

Quindi le seguenti affermazioni sono eguali:

t2.start_date between t1.start_date and t1.end_date 

e

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

L'SQL che stai cercando verrà seguenti:

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 
1

In SQL, il modo migliore per esprimere l'intersezione degli intervalli che ho trovato è:

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

Ogni caso di intersezione cade in una di queste due condizioni.

1

ho scelto modo non è molto semplice, senza grandi prestazioni =)

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) 

cerco di coprire i casi di seguito:

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

Io in realtà non so cosa vuoi dire e che cosa si voleva raggiungere qui, ma spero che questo sarebbe di aiuto a voi. In qualche modo, sono stato in grado di ottenere l'output che ti riferivi (in parte). Basta fare un piccolo ritocco su questo codice.

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' 

DA tbl1, TBL2 dove (CONVERT (DATA, tbl2.START)> = CONVERTI (DATA, tbl1.START)) E (CONVERT (DATA, TBL2. [FINE]) < = Convert (DATE, tbl1. [END]))