select 以 5 分钟为间隔对日期和时间进行分组

select to group date and time in 5-minute intervals

我希望计算 table 按日期和时间间隔 5 分钟分组的行数:

例如,如果 HH:MM 的分钟部分介于 00 分钟和 04 分钟之间,它将被计为 00,例如。 08:04 将被计算为 08:00

如果分钟部分介于 05 分钟和 09 分钟之间,它将被计为 05,例如。 08:06 将被计为 08:05

Table数据

Date       Time
18/01/18    08:00
18/01/18    08:01
18/01/18    08:02
18/01/18    08:03
18/01/18    08:04

18/01/18    08:05
18/01/18    08:06
18/01/18    08:08

18/01/18    08:10

19/01/18    17:01
19/01/18    17:03
19/01/18    17:04

预期输出

DATE         TIME  COUNT
18/01/2018   08:00   5
18/01/2018   08:05   3
18/01/2018   08:10   1
19/01/2018   17:00   3

Table 创作

创建table TAB1 (tDATE DATE,tTIME VARCHAR2(5));

数据

insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:00');

insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:01');

insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:02');

insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:03');

insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:04');

insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:05');

insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:06');

insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:08');


insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:10');

insert into TAB1(tDATE,tTIME) values (to_date('19/01/2018','DD/MM/YYYY'),'17:01');

insert into TAB1(tDATE,tTIME) values (to_date('19/01/2018','DD/MM/YYYY'),'17:03');

insert into TAB1(tDATE,tTIME) values (to_date('19/01/2018','DD/MM/YYYY'),'17:04');

这在Oracle中有点繁琐,但是用字符串算法就很可行了:

select date, 
       substring(time, 1, 3) || lpad(floor(cast(substring(time, -2) as number) / 12) * 12, 2, '0') as time,
       count(*)
from tab1
group by date, 
         substring(time, 1, 3) || lpad(floor(cast(substring(time, -2) as number) / 12) * 12, 2, '0')
order by date, time;

试试这个,

select tdate, 
       SUBSTR(ttime, 1, 2)||':'||
              LPAD(NVL(DECODE(SIGN(ROUND(SUBSTR(ttime, 4, 2), -1)-SUBSTR(ttime, 4, 2)), 
                                 1, ROUND(SUBSTR(ttime, 4, 2), -1)-5,
                                -1, ROUND(SUBSTR(ttime, 4, 2), -1)), 0),2, '0') time_,
       count(1)
  from tab1
 group by tdate, SUBSTR(ttime, 1, 2)||':'||
                 LPAD(NVL(DECODE(SIGN(ROUND(SUBSTR(ttime, 4, 2), -1)-SUBSTR(ttime, 4, 2)), 
                                     1, ROUND(SUBSTR(ttime, 4, 2), -1)-5,
                                    -1, ROUND(SUBSTR(ttime, 4, 2), -1)), 0),2, '0');

每当我需要这样的时间间隔时,我都会使用这个通用函数:

CREATE OR REPLACE FUNCTION MakeInterval(ts IN TIMESTAMP, roundInterval IN INTERVAL DAY TO SECOND) RETURN TIMESTAMP DETERMINISTIC IS
    denom INTEGER;
BEGIN
    IF roundInterval >= INTERVAL '1' HOUR THEN
        denom := EXTRACT(HOUR FROM roundInterval);
        IF MOD(24, denom) <> 0 THEN
            RAISE VALUE_ERROR;
        END IF;
        RETURN TRUNC(ts) + TRUNC(EXTRACT(HOUR FROM ts) / denom) * denom * INTERVAL '1' HOUR;
    ELSIF roundInterval >= INTERVAL '1' MINUTE THEN
        denom := EXTRACT(MINUTE FROM roundInterval);
        IF MOD(60, denom) <> 0 THEN
            RAISE VALUE_ERROR;
        END IF;
        RETURN TRUNC(ts, 'hh') + TRUNC(EXTRACT(MINUTE FROM ts) / denom) * denom * INTERVAL '1' MINUTE;
    ELSE
        denom := EXTRACT(SECOND FROM roundInterval);                
        IF MOD(60, denom) <> 0 THEN
            RAISE VALUE_ERROR;
        END IF;
        RETURN TRUNC(ts, 'mi') + TRUNC(EXTRACT(SECOND FROM ts) / denom) * denom * INTERVAL '1' SECOND;
    END IF;
END MakeInterval;

有效间隔为:1,2,3,4,5,6,10,12,15,20,30,60 SECOND, 1,2,3,4,5,6,10,12, 15、20、30、60 分钟,1、2、3、4、6、8、12 小时

您将时间存储在单独的列中,这是一个糟糕的设计。首先,设置一个合适的 DATETIMESTAMP 值,例如: TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI')

那你可以这样用

SELECT 
   MakeInterval(TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI'), INTERVAL '5' MINUTE), ...

当然,如果你不喜欢使用单独的函数,你可以把所有的都放在一行中:

TRUNC(TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI'), 'hh') + TRUNC(EXTRACT(MINUTE FROM TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI')) / 5) * INTERVAL '5' MINUTE;

谢谢大家。至于为什么时间组件有一个单独的 VARCHAR2 列,这些表最初是从一些具有日期类型但没有时间组件的遗留数据库迁移的,后者存储为字符串。这是我自己的想法,正是我想要的:

select tDATE,substr(tTIME,1,3)||
         case 
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 0 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 5 then '00' 
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 5 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 10 then '05'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 10 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 15 then '10'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 15 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 20 then '15'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 20 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 25 then '20'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 25 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 30 then '25'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 30 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 35 then '30'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 35 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 40 then '35'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 40 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 45 then '40'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 45 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 50 then '45'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 50 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 55 then '50'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 55 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 60 then '55'
           else '00'
         end as tTIME
,count(*)
from TAB1
group by tDATE,substr(tTIME,1,3)||
         case 
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 0 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 5 then '00' 
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 5 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 10 then '05'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 10 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 15 then '10'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 15 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 20 then '15'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 20 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 25 then '20'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 25 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 30 then '25'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 30 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 35 then '30'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 35 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 40 then '35'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 40 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 45 then '40'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 45 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 50 then '45'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 50 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 55 then '50'
           when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 55 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 60 then '55'
           else '00'
         end
order by 3 desc;