【问题标题】:Oracle: Get every half hour between two timesOracle:在两次之间每半小时获取一次
【发布时间】:2014-02-19 18:49:10
【问题描述】:

我有一些数据定义了一个时间范围。

CREATE TABLE MY_TIME_TABLE
(
    MY_PK     NUMBER(10)  NOT NULL ENABLE,
    FROM_TIME DATE        NOT NULL ENABLE,
    TO_TIME   DATE        NOT NULL ENABLE
);

INSERT INTO MY_TIME_TABLE(MY_PK,FROM_TIME,TO_TIME)
VALUES(1,TO_DATE('2014-01-01 09:00:00', 'YYYY-MM-DD HH24:MI:SS'),TO_DATE('2014-01-01 13:00:00', 'YYYY-MM-DD HH24:MI:SS');

INSERT INTO MY_TIME_TABLE(MY_PK,FROM_TIME,TO_TIME)
VALUES(2,TO_DATE('2014-01-02 14:00:00', 'YYYY-MM-DD HH24:MI:SS'),TO_DATE('2014-01-02 15:00:00', 'YYYY-MM-DD HH24:MI:SS');

INSERT INTO MY_TIME_TABLEMY_PK,(FROM_TIME,TO_TIME)
VALUES(3,TO_DATE('2014-01-03 00:30:00', 'YYYY-MM-DD HH24:MI:SS'),TO_DATE('2014-01-03 03:30:00', 'YYYY-MM-DD HH24:MI:SS');

我想做的是创建一个查询,该查询将返回两次之间的所有半小时块。所以它会返回如下内容:

1, 2014-01-01 09:00:00
1, 2014-01-01 09:30:00
1, 2014-01-01 10:00:00
1, 2014-01-01 10:30:00
1, 2014-01-01 11:00:00
1, 2014-01-01 11:30:00
1, 2014-01-01 12:00:00
1, 2014-01-01 12:30:00
2, 2014-01-02 14:00:00
2, 2014-01-02 14:30:00
3, 2014-01-03 00:30:00
3, 2014-01-03 01:00:00
3, 2014-01-03 01:30:00
3, 2014-01-03 02:00:00
3, 2014-01-03 02:30:00
3, 2014-01-03 03:00:00

数据保证每小时或半小时开始和结束,所以我不必担心部分匹配。

我通常会尝试展示自己为解决问题所做的工作,但在这种情况下,我什至不知道从哪里开始。

【问题讨论】:

  • 寻找CTE——这是生成一系列数据(即日期)的常用方法。
  • 您可以通过减去日期和 div 分钟数除以 30 来找到周期数,然后使用 ((number table * period in between) * 30) 将分钟数添加回开始时间以创建时间戳。
  • 啊,我还没有遇到CTE。我一定会调查的。 bd33:这听起来也可以。不过,我将把它合并到一个更大的查询中,并且有些事情告诉我这可能会很快变得复杂。我想,直到我试一试才知道。
  • 你用的是什么oracle版本?
  • 我正在使用 11g R2 进行开发,但它也必须在 10g R2 上运行。

标签: sql oracle date


【解决方案1】:

您可以使用分层查询或 CTE 来完成。

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE MY_TIME_TABLE ( MY_PK, FROM_TIME, TO_TIME ) AS
SELECT  1, TO_DATE('2014-01-01 09:00:00', 'YYYY-MM-DD HH24:MI:SS'), TO_DATE('2014-01-01 13:00:00', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL
UNION ALL
SELECT  2, TO_DATE('2014-01-02 14:00:00', 'YYYY-MM-DD HH24:MI:SS'), TO_DATE('2014-01-02 15:00:00', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL
UNION ALL
SELECT  3, TO_DATE('2014-01-03 00:30:00', 'YYYY-MM-DD HH24:MI:SS'), TO_DATE('2014-01-03 03:30:00', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL;

分层查询

SELECT MY_PK, FROM_TIME + (LEVEL-1) / 48
FROM MY_TIME_TABLE
CONNECT BY LEVEL <= (TO_TIME - FROM_TIME) * 48
          AND PRIOR MY_PK = MY_PK
          AND PRIOR dbms_random.value IS NOT NULL

Results

| MY_PK |         FROM_TIME+(LEVEL-1)/48 |
|-------|--------------------------------|
|     1 | January, 01 2014 09:00:00+0000 |
|     1 | January, 01 2014 09:30:00+0000 |
|     1 | January, 01 2014 10:00:00+0000 |
|     1 | January, 01 2014 10:30:00+0000 |
|     1 | January, 01 2014 11:00:00+0000 |
|     1 | January, 01 2014 11:30:00+0000 |
|     1 | January, 01 2014 12:00:00+0000 |
|     1 | January, 01 2014 12:30:00+0000 |
|     2 | January, 02 2014 14:00:00+0000 |
|     2 | January, 02 2014 14:30:00+0000 |
|     3 | January, 03 2014 00:30:00+0000 |
|     3 | January, 03 2014 01:00:00+0000 |
|     3 | January, 03 2014 01:30:00+0000 |
|     3 | January, 03 2014 02:00:00+0000 |
|     3 | January, 03 2014 02:30:00+0000 |
|     3 | January, 03 2014 03:00:00+0000 |

【讨论】:

  • 我喜欢这个查询的简单性!这将使将其合并到我正在构建的更大查询中变得容易。我不知道它在速度方面与其他解决方案相比如何,但在这种特殊情况下,至少简单性绝对是更重要的因素。谢谢!
【解决方案2】:

如果您使用的是 11gR2,则可以使用 recursive subquery factoring(又名递归 CTE 或递归 with):

with r (my_pk, from_time, to_time) as (
  select my_pk, from_time, to_time
  from my_time_table
  union all
  select my_pk, from_time + interval '30' minute, to_time
  from r
  where from_time + interval '30' minute < to_time
)
select my_pk, to_char(from_time, 'YYYY-MM-DD HH24:MI:SS') as from_time
from r
order by my_pk, from_time;

     MY_PK FROM_TIME         
---------- -------------------
         1 2014-01-01 09:00:00                       
         1 2014-01-01 09:30:00                       
         1 2014-01-01 10:00:00                       
         1 2014-01-01 10:30:00                       
         1 2014-01-01 11:00:00                       
         1 2014-01-01 11:30:00                       
         1 2014-01-01 12:00:00                       
         1 2014-01-01 12:30:00                       
         2 2014-01-02 14:00:00                       
         2 2014-01-02 14:30:00                       
         3 2014-01-03 00:30:00                       
         3 2014-01-03 01:00:00                       
         3 2014-01-03 01:30:00                       
         3 2014-01-03 02:00:00                       
         3 2014-01-03 02:30:00                       
         3 2014-01-03 03:00:00  

anchor 子句获取每个 PK 值的开始时间,递归部分不断增加 30 分钟的间隔,直到达到该 PK 的结束时间。然后,您可以将该 CTE 用作查询中其他地方的源表;这里我只是显示内容,很清楚。

根据您将如何使用这些范围,您可能会发现生成每个半小时块的结尾也很有用,例如用于主查询中的between 子句:

with r (my_pk, from_time, to_time, max_time) as (
  select my_pk, from_time,
    from_time + interval '30' minute - interval '1' second, to_time
  from my_time_table
  union all
  select my_pk, from_time + interval '30' minute,
    to_time + interval '30' minute, max_time
  from r
  where from_time + interval '30' minute < max_time
)
select my_pk, to_char(from_time, 'YYYY-MM-DD HH24:MI:SS') as from_time,
  to_char(to_time, 'YYYY-MM-DD HH24:MI:SS') as to_time
from r
order by my_pk, from_time;

    MY_PK FROM_TIME           TO_TIME           
---------- ------------------- -------------------
         1 2014-01-01 09:00:00 2014-01-01 09:29:59 
         1 2014-01-01 09:30:00 2014-01-01 09:59:59 
         1 2014-01-01 10:00:00 2014-01-01 10:29:59 
         1 2014-01-01 10:30:00 2014-01-01 10:59:59 
         1 2014-01-01 11:00:00 2014-01-01 11:29:59 
         1 2014-01-01 11:30:00 2014-01-01 11:59:59 
         1 2014-01-01 12:00:00 2014-01-01 12:29:59 
         1 2014-01-01 12:30:00 2014-01-01 12:59:59 
         2 2014-01-02 14:00:00 2014-01-02 14:29:59 
         2 2014-01-02 14:30:00 2014-01-02 14:59:59 
         3 2014-01-03 00:30:00 2014-01-03 00:59:59 
         3 2014-01-03 01:00:00 2014-01-03 01:29:59 
         3 2014-01-03 01:30:00 2014-01-03 01:59:59 
         3 2014-01-03 02:00:00 2014-01-03 02:29:59 
         3 2014-01-03 02:30:00 2014-01-03 02:59:59 
         3 2014-01-03 03:00:00 2014-01-03 03:29:59 

【讨论】:

    【解决方案3】:
    select my_pk, from_time + (half_hour_increment-1) * interval '30' minute new_time
    from
    (
        select my_pk, from_time, to_time, (to_time-from_time)*24*2 half_hours
        from my_time_table
    ) my_time_table
    join
    (
        select level half_hour_increment
        from
        (
            select max(to_time-from_time)*24*2 max_half_hours
            from my_time_table
        )
        connect by level <= max_half_hours
    ) row_generator
        on half_hours >= half_hour_increment
    order by my_pk, half_hour_increment;
    

    【讨论】:

      猜你喜欢
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 2010-12-20
      • 2021-10-23
      • 1970-01-01
      • 2016-04-29
      • 1970-01-01
      相关资源
      最近更新 更多