生成具有时间戳范围的 table - Oracle SQL

Generate a table with a range of timestamps - Oracle SQL

我正在尝试创建一个 table,其中包含以下格式的 2 列以及 2019 年的所有日期:-

      START_TIME          END_TIME
2010-01-01 17:00:00|2019-01-02 17:00:00
2019-01-02 17:00:00|2019-01-03 17:00:00
2019-01-03 17:00:00|2019-01-04 17:00:00
...
...
2019-12-31 17:00:00|2020-01-01 17:00:00

能否请您帮忙排查其中的错误? 请提出实现此目的的任何优化方法。

CREATE TABLE s.dates_2019
(
ts_range_begin timestamp(6),
ts_range_end timestamp(6),

);


 insert into s.dates_2019 (ts_range_begin)
 select 
     to_timestamp('12/31/2018 05:00 PM', 'YYYY-MM-DD HH24:MI:SS') + n.n
 from
  (select rownum n
   from   ( select 1 just_a_column
         from   dual
         connect by level <=
                to_timestamp('12/31/2019 05:00 PM', 'YYYY-MM-DD HH24:MI:SS') 
               -  to_timestamp('12/31/2018 05:00 PM', 'YYYY-MM-DD HH24:MI:SS')  
               + 1
           ) t
  ) n
  where
       to_timestamp('12/31/2018 05:00 PM','YYYY-MM-DD HH24:MI:SS')  + n.n <=  to_timestamp('12/31/2019 05:00 PM','YYYY-MM-DD HH24:MI:SS') 

 insert into s.dates_2019 (ts_range_end)
 select 
     to_timestamp('2019-01-01 05:00 PM', 'YYYY-MM-DD HH24:MI:SS') + n.n
 from
  (select rownum n
   from   ( select 1 just_a_column
         from   dual
         connect by level <=
                to_timestamp('2020-01-01 05:00 PM', 'YYYY-MM-DD HH24:MI:SS') 
               -  to_timestamp('2019-01-01 05:00 PM', 'YYYY-MM-DD HH24:MI:SS')  
               + 1
           ) t
  ) n
  where
       to_timestamp('2019-01-01 05:00 PM','YYYY-MM-DD HH24:MI:SS')  + n.n <=  to_timestamp('2020-01-01 05:00 PM','YYYY-MM-DD HH24:MI:SS')

错误是:- [Error Code: 30081, SQL State: 99999] ORA-30081: invalid data type for datetime/interval arithmetic

这个怎么样?

SQL> alter session set nls_date_format = 'yyyy-mm-dd hh24:mi';

Session altered.

SQL> with dates as
  2    (select date '2019-01-01' + 17/24 + level - 1 datum
  3     from dual
  4     connect by level <= date '2020-01-01' - date '2019-01-01' + 1
  5    ),
  6  staend as
  7    (select datum as start_time,
  8            lead(datum) over (order by datum) as end_time
  9     from dates
 10    )
 11  select start_time,
 12         end_time
 13  from staend
 14  where end_time is not null
 15  order by start_time;

START_TIME       END_TIME
---------------- ----------------
2019-01-01 17:00 2019-01-02 17:00
2019-01-02 17:00 2019-01-03 17:00
2019-01-03 17:00 2019-01-04 17:00
2019-01-04 17:00 2019-01-05 17:00
<snip>
2019-12-30 17:00 2019-12-31 17:00
2019-12-31 17:00 2020-01-01 17:00

365 rows selected.

SQL>

如果您想将日期插入 table,您实际上不需要 timestamp - date 就可以了。

SQL> create table dates_2019
  2    (ts_range_begin date,
  3     ts_range_end   date
  4    );

Table created.

SQL> insert into dates_2019 (ts_range_begin, ts_range_end)
  2  with dates as
  3    (select date '2019-01-01' + 17/24 + level - 1 datum
  4     from dual
  5     connect by level <= date '2020-01-01' - date '2019-01-01' + 1
  6    ),
  7  staend as
  8    (select datum as start_time,
  9            lead(datum) over (order by datum) as end_time
 10     from dates
 11    )
 12  select start_time,
 13         end_time
 14  from staend
 15  where end_time is not null
 16  order by start_time;

365 rows created.

SQL>

如果要聚合周末,请考虑在 lead 分析函数中使用 offset。该偏移量取决于日期名称(星期五)。此外,从结果集中删除周末(第 21 行,where day not in ('sat', 'sun'))。

SQL> insert into dates_2019 (ts_range_begin, ts_range_end)
  2  with dates as
  3    (select date '2019-01-01' + 17/24 + level - 1 datum,
  4            --
  5            to_char(date '2019-01-01' + 17/24 + level - 1,
  6                    'fmdy', 'nls_date_language = english') day
  7     from dual
  8     connect by level <= date '2020-01-01' - date '2019-01-01' + 1
  9    ),
 10  staend as
 11    (select datum as start_time,
 12            day,
 13            lead(datum, case when day = 'fri' then 3
 14                             else 1
 15                        end) over (order by datum) as end_time
 16     from dates
 17    )
 18  select start_time,
 19         end_time
 20  from staend
 21  where day not in ('sat', 'sun')
 22    and end_time is not null;

261 rows created.

SQL> select * from dates_2019 order by ts_range_begin;

TS_RANGE_BEGIN   TS_RANGE_END
---------------- ----------------
2019-01-01 17:00 2019-01-02 17:00
2019-01-02 17:00 2019-01-03 17:00
2019-01-03 17:00 2019-01-04 17:00
2019-01-04 17:00 2019-01-07 17:00     --> aggregated
2019-01-07 17:00 2019-01-08 17:00
2019-01-08 17:00 2019-01-09 17:00
2019-01-09 17:00 2019-01-10 17:00
2019-01-10 17:00 2019-01-11 17:00
2019-01-11 17:00 2019-01-14 17:00     --> aggregated
2019-01-14 17:00 2019-01-15 17:00
2019-01-15 17:00 2019-01-16 17:00
<snip>

我认为您的实际错误是因为减去时间戳 returns 一个间隔,而您将结果用作 CONNECT BY LEVEL 中的数字。您可以将时间戳转换为日期(您可能会发现 the answers here useful) or use an interval expression 以获取时间戳之间的日期部分。

但如果这是您的实际 SQL 而不是简化,我建议只在 CONNECT BY 中使用日期(如果您是这样的话,您仍然可以在 table 中保留时间戳想要)并做类似...

CREATE TABLE dates_2019
(
ts_range_begin timestamp(6),
ts_range_end timestamp(6)
);

insert into dates_2019 (ts_range_begin)
 select 
     to_timestamp('2018-12-31 17', 'YYYY-MM-DD HH24') + rownum
 from
     dual
 connect by level <= to_date('2019-12-31 17', 'YYYY-MM-DD HH24') - to_date('2018-12-31 17', 'YYYY-MM-DD HH24')
 ;

update dates_2019 SET ts_range_end = ts_range_begin + 1;

...我在 Oracle 18c 中测试过,但可能适用于 10g。