生成具有一系列时间戳的表 - Oracle SQL

Posted

技术标签:

【中文标题】生成具有一系列时间戳的表 - Oracle SQL【英文标题】:Generate a table with a range of timestamps - Oracle SQL 【发布时间】:2020-04-23 04:53:58 【问题描述】:

我正在尝试使用以下格式创建一个包含 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

【问题讨论】:

to_timestamp('12/31/2018 05:00 PM', 'YYYY-MM-DD HH24:MI:SS') 格式错误。字符串12/31/2018 05:00 PM 不是YYYY-MM-DD HH24:MI:SS,它们必须匹配。 【参考方案1】:

这个怎么样?

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>

如果你想在表格中插入日期,你真的不需要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>

【讨论】:

谢谢..有没有一个函数可以用来聚合周末日期(周五到周一),如下所示:- `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-07 17:00 ` 不客气。我添加了一些聚合周末的代码。请看一下。 非常感谢..这完全解决了我的用例。 没问题,如果有帮助我很高兴。【参考方案2】:

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

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

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。

【讨论】:

以上是关于生成具有一系列时间戳的表 - Oracle SQL的主要内容,如果未能解决你的问题,请参考以下文章

SQL时间戳的使用

PostgreSQL:为所有行创建具有唯一时间戳的表

将具有当前时间戳的列添加到 Hive 中的表

用于从表中选择具有最新时间戳的行的 JOOQ 代码

用于每行唯一时间戳的 Oracle Sql 语句

从 Oracle PL/SQL 中的表生成 json 文件