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

时间:2018-01-22 23:37:45

标签: sql oracle group-by intervals

我希望按照5分钟的日期和时间间隔计算表格的行数:

例如,如果HH:MM的分钟部分落在00分钟和04分钟之间,则它将被计为00,例如。 08:04将被视为08:00

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

表格数据

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

表格创建

创建表格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');

4 个答案:

答案 0 :(得分:1)

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

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;

答案 1 :(得分:0)

这在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;

答案 2 :(得分:0)

试试这个,

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');

答案 3 :(得分:0)

谢谢大家。至于为什么时间组件有一个单独的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;