2010-04-06 9 views
5

Uso di Oracle 11g Release 2, la seguente query dà un ORA-01790: espressione deve avere lo stesso tipo di dati come espressione corrispondente:SQL ricorsivo dando ORA-01790

with intervals(time_interval) AS 
(select trunc(systimestamp) 
    from dual 
    union all 
    select (time_interval + numtodsinterval(10, 'Minute')) 
    from intervals 
    where time_interval < systimestamp) 
select time_interval from intervals; 

L'errore indica che il tipo di dati di entrambi sottoquery di l'UNION ALL restituisce diversi tipi di dati.

Anche se si esegue il cast in TIMESTAMP in ciascuna subquery, si ottiene lo stesso errore.

Cosa mi manca?

MODIFICA: Non sto cercando una sostituzione CONNECT BY.

+0

Qual è il tipo di 'time_interval' e perché il titolo dice * * ricorsivo? –

+0

@Peter: la vista WITH fa riferimento a se stessa. Questo è nuovo in 11gR2. vedere http://download.oracle.com/docs/cd/E11882_01/server.112/e10881/chapter1.htm#FEATURENO08835 – PenFold

+0

Impossibile visualizzarlo, mi dispiace. –

risposta

6

A mio parere, "ricorsivo Sottoquery Factoring" è broken in 11g R2 per le query con la colonna data o timestamp.

with test(X) as 
(
    select to_date('2010-01-01','YYYY-MM-DD') from dual 
    union all (
    select (X + 1) from test where X <= to_date('2010-01-10','YYYY-MM-DD') 
) 
) 
select * from test; 

ORA-01790 

uso un cast per convertire il tipo di dati:

with test(X) as 
(
    select cast(to_date('2010-01-01','YYYY-MM-DD') as date) from dual 
    union all (
    select (X + 1) from test where X <= to_date('2010-01-10','YYYY-MM-DD') 
) 
) 
select * from test; 

X 
------------------- 
2010-01-01 00:00:00 

1 row selected 

Casting una data in una data sta aiutando, ma dove sono gli altri risultati?

Ottiene ancora migliore ...

provare con un'altra data di inizio:

with test(X) as 
(
    select cast(to_date('2007-01-01','YYYY-MM-DD') as DATE) from dual 
    union all (
    select (X + 1) from test where X <= to_date('2011-01-11','YYYY-MM-DD') 
) 
) 
select * from test 
where rownum < 10; -- important! 

X 
------------------- 
2007-01-01 00:00:00 
2006-12-31 00:00:00 
2006-12-30 00:00:00 
2006-12-29 00:00:00 
2006-12-28 00:00:00 
2006-12-27 00:00:00 
2006-12-26 00:00:00 
2006-12-25 00:00:00 
2006-12-24 00:00:00 

9 rows selected 

conto alla rovescia? Perché?

Aggiornamento 14-gen-2014: Per risolvere il problema, utilizzare il CTE a partire dalla data di fine e la costruzione della CTE ricorsiva a ritroso, in questo modo:

with test(X) as 
(
    select cast(to_date('2011-01-20','YYYY-MM-DD') as DATE) as x from dual 
    union all (
    select cast(X - 1 AS DATE) from test 
    where X > to_date('2011-01-01','YYYY-MM-DD') 
) 
) 
select * from test 

Risultati:

|        X | 
|--------------------------------| 
| January, 20 2011 00:00:00+0000 | 
| January, 19 2011 00:00:00+0000 | 
| January, 18 2011 00:00:00+0000 | 
| January, 17 2011 00:00:00+0000 | 
| January, 16 2011 00:00:00+0000 | 
| January, 15 2011 00:00:00+0000 | 
| January, 14 2011 00:00:00+0000 | 
| January, 13 2011 00:00:00+0000 | 
| January, 12 2011 00:00:00+0000 | 
| January, 11 2011 00:00:00+0000 | 
| January, 10 2011 00:00:00+0000 | 
| January, 09 2011 00:00:00+0000 | 
| January, 08 2011 00:00:00+0000 | 
| January, 07 2011 00:00:00+0000 | 
| January, 06 2011 00:00:00+0000 | 
| January, 05 2011 00:00:00+0000 | 
| January, 04 2011 00:00:00+0000 | 
| January, 03 2011 00:00:00+0000 | 
| January, 02 2011 00:00:00+0000 | 
| January, 01 2011 00:00:00+0000 | 

Test condotto con:

Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production 
0

Non ho idea circa il tipo non corrispondente, ma qui è un metodo alternativo per realizzare quello che penso che vuoi (che funziona in 10gR2):

select base_time + numtodsinterval(10*(level-1), 'Minute') 
from (select trunc(systimestamp) base_time from dual) 
connect by base_time + numtodsinterval(10*(level-1), 'Minute') < systimestamp 
+0

Grazie a Dave, ma sto cercando una soluzione che usi la sintassi sql ricorsiva più pulita. – PenFold

2

Dispari - wor ks Se si passa intorno varchar s e convertire (non espressi):

WITH intervals(time_interval) AS 
    (SELECT to_char(TRUNC(systimestamp)) 
    FROM dual 
    UNION ALL 
    SELECT to_char(to_timestamp(time_interval) + numtodsinterval(10, 'Minute')) 
    FROM intervals 
    WHERE to_timestamp(time_interval) < systimestamp 
) 
SELECT to_timestamp(time_interval) time_interval 
FROM intervals