Sql al connect by level@mcalex我首先试图找出一个小时内花费的总分钟数,然后我按开始时间字段分组到开始时间字符(开始时间,'HH24'),以得到错误的结果。我知道查询应该包括一些计算,但缺乏关于如何提取所需结果的足够知识。第二行和第三行

Sql al connect by level@mcalex我首先试图找出一个小时内花费的总分钟数,然后我按开始时间字段分组到开始时间字符(开始时间,'HH24'),以得到错误的结果。我知道查询应该包括一些计算,但缺乏关于如何提取所需结果的足够知识。第二行和第三行,sql,oracle,oracle10g,group-by,date-arithmetic,Sql,Oracle,Oracle10g,Group By,Date Arithmetic,al connect by level@mcalex我首先试图找出一个小时内花费的总分钟数,然后我按开始时间字段分组到开始时间字符(开始时间,'HH24'),以得到错误的结果。我知道查询应该包括一些计算,但缺乏关于如何提取所需结果的足够知识。第二行和第三行-有64分钟以上:上午到下午。@FlorinGhita我更正了它,谢谢你让我知道!您是否在另一个表中定义了范围?问题很清楚。只需要一些时间就可以得到一个好的答案。这怎么可能是一个答案呢?我必须找出每天30分钟范围内按每种代码分组的总和(分钟)。


al connect by level@mcalex我首先试图找出一个小时内花费的总分钟数,然后我按开始时间字段分组到开始时间字符(开始时间,'HH24'),以得到错误的结果。我知道查询应该包括一些计算,但缺乏关于如何提取所需结果的足够知识。第二行和第三行-有64分钟以上:上午到下午。@FlorinGhita我更正了它,谢谢你让我知道!您是否在另一个表中定义了范围?问题很清楚。只需要一些时间就可以得到一个好的答案。这怎么可能是一个答案呢?我必须找出每天30分钟范围内按每种代码分组的总和(分钟)。请看预期输出!这个例子可能有用,但上面的文字不是答案。这几乎奏效了,但如果你签入Fiddle,11月1日7:30到8:00范围内的代码C2的分钟数是30而不是60!哎呀。这与案例陈述中的
之间的
有关。我现在已经纠正了这一点,所以相关的范围是60。这是最容易理解的,它就像一个符咒!你太棒了!:)
Emp  Date       START_TIME            END_TIME             Code     Minutes
---  --------   -------------------   -------------------  ----     -------
 E1  11/1/2012  11/1/2012 6:55:00 AM  11/1/2012 7:01:00 AM  C1       6
 E1  11/1/2012  11/1/2012 6:57:00 AM  11/1/2012 8:01:00 AM  C2       64
 E2  11/1/2012  11/1/2012 6:57:00 AM  11/1/2012 8:00:00 AM  C2       63
 E1  11/2/2012  11/2/2012 7:35:00 AM  11/2/2012 8:01:00 AM  C1       26
Date       Code  Range                        Minutes
---------  ----  -----------------------      ------- 
11/1/2012   C1   6:30:00 AM-7:00:00 AM           5
11/1/2012   C1   7:00:00 AM-7:30:00 AM           1
11/1/2012   C2   6:30:00 AM-7:00:00 AM           6   
11/1/2012   C2   7:00:00 AM-7:30:00 AM           60
11/1/2012   C2   7:30:00 AM-8:00:00 AM           60
11/1/2012   C2   8:00:00 AM-8:30:00 AM           1
11/2/2012   C1   7:30:00 AM-8:00:00 AM           25
11/2/2012   C1   8:00:00 AM-8:30:00 AM           1
SELECT job
  , sum(decode(greatest(sal,2999), least(sal,6000), 1, 0)) "Range 3000-6000"
  , sum(decode(greatest(sal,1000), least(sal,2999), 1, 0)) "Range 1000-3000"
  , sum(decode(greatest(sal,0),    least(sal,999), 1, 0))  "Range 0-1000"
  FROM scott.emp
GROUP BY  job
/
SELECT  "Date",
        "Code",
        "RangeStart" + ((r - 1) / 48.0) AS "RangeStart",
        "RangeStart" + (r / 48.0) AS "RangeEnd",
        SUM(CASE WHEN r = 1 THEN "StartMinutes"
                WHEN "END_TIME" >= "RangeStart" + ((r - 1) / 48.0) AND "END_TIME" < "RangeStart" + (r / 48.0) THEN "EndMinutes"
                ELSE 30 
            END) AS "TotalMinutes"
FROM    (   SELECT  "Emp",
                    "Date",
                    "START_TIME",
                    "END_TIME",
                    "Code",
                    CASE WHEN EXTRACT(MINUTE from "START_TIME") > 30 THEN 60 ELSE 30 END - EXTRACT(MINUTE from "START_TIME") AS "StartMinutes",
                    EXTRACT(MINUTE from END_TIME) - CASE WHEN EXTRACT(MINUTE from "END_TIME") > 30 THEN 30 ELSE 0 END AS "EndMinutes",
                    "START_TIME" - (EXTRACT(MINUTE from "START_TIME") - CASE WHEN EXTRACT(MINUTE from "START_TIME") > 30 THEN 30 ELSE 0 END) / (60 * 24.0) AS "RangeStart"
            FROM    T
        ) T
        INNER JOIN
        (   SELECT  Rownum r
            FROM    dual
            CONNECT BY Rownum <= 100
        ) r
            ON "END_TIME" > ("RangeStart" + ((r - 1) / 48.0))
GROUP BY "Date", "Code", "RangeStart" + ((r - 1) / 48.0), "RangeStart" + (r / 48.0)
ORDER BY "Code", "Date", "RangeStart";
SQL> select emp, start_time, end_time, code,
  2                      trunc(start_time, 'mi')
  3                      - (mod(to_char(trunc(start_time, 'mi'), 'mi'), 30) / 1440) start_block,
  4                      ceil(2*24*(end_time-(trunc(start_time, 'mi')
  5                      - (mod(to_char(trunc(start_time, 'mi'), 'mi'), 30) / 1440)))) blocks
  6                 from tab f
  7  /

EM START_TIME             END_TIME               CO START_BLOCK                BLOCKS
-- ---------------------- ---------------------- -- ---------------------- ----------
E1 11/01/2012 06:55:00 am 11/01/2012 07:01:00 am C1 11/01/2012 06:30:00 am          2
E1 11/01/2012 06:57:00 am 11/01/2012 08:01:00 am C2 11/01/2012 06:30:00 am          4
E2 11/01/2012 06:57:00 am 11/01/2012 08:00:00 am C2 11/01/2012 06:30:00 am          3
E1 11/02/2012 07:35:00 am 11/02/2012 08:01:00 am C1 11/02/2012 07:30:00 am          2
SQL> with foo as (select rownum id, emp, start_time, end_time, code,
  2                      trunc(start_time, 'mi')
  3                      - (mod(to_char(trunc(start_time, 'mi'), 'mi'), 30) / 1440) start_block,
  4                      ceil(2*24*(end_time-(trunc(start_time, 'mi')
  5                      - (mod(to_char(trunc(start_time, 'mi'), 'mi'), 30) / 1440)))) blocks
  6                 from tab f)
  7  select trunc(start_time) thedate, code, emp, range, minutes
  8    from foo
  9   model partition by(id)
 10         dimension by(0 as f)
 11         measures(code, emp, start_time, end_time, start_block, blocks,
 12                  sysdate as start_range,
 13                  sysdate as end_range,
 14                  cast(0 as number) minutes,
 15                  cast('' as varchar2(50)) range)
 16         rules (start_range [for f from 0 to blocks[0]-1  increment 1]  = start_block[0] + (30*cv(f)/1440),
 17                end_range[any]  =  start_range[cv()] + (30/1440),
 18                code[any]  =  code[0],
 19                emp[any]   =  emp[0],
 20                start_time[any]  =  start_time[0],
 21                end_time[any]  =  end_time[0],
 22                range [any] = to_char(start_range[cv()], 'dd/mm/yyyy hh:mi:ss am') || ' - ' || to_char(end_range[cv()], 'dd/mm/yyyy hh24:mi:ss am'),
 23                minutes [any]   = case
 24                                    when start_time[0] between start_range[cv()] and end_range[cv()]
 25                                    then 1440 *(end_range[cv()] - start_time[0])
 26                                    when end_time[0] between start_range[cv()] and end_range[cv()]
 27                                    then 1440 *(end_time[0] - start_range[cv()])
 28                                    else 1440 * (end_range[cv()] - start_range[cv()])
 29                                  end );

CO EM RANGE                                                 MINUTES
-- -- -------------------------------------------------- ----------
C2 E2 11/01/2012 06:30:00 am - 11/01/2012 07:00:00 am             3
C2 E2 11/01/2012 07:00:00 am - 11/01/2012 07:30:00 am            30
C2 E2 11/01/2012 07:30:00 am - 11/01/2012 08:00:00 am            30
C1 E1 11/01/2012 06:30:00 am - 11/01/2012 07:00:00 am             5
C1 E1 11/01/2012 07:00:00 am - 11/01/2012 07:30:00 am             1
C1 E1 11/02/2012 07:30:00 am - 11/02/2012 08:00:00 am            25
C1 E1 11/02/2012 08:00:00 am - 11/02/2012 08:30:00 am             1
C2 E1 11/01/2012 06:30:00 am - 11/01/2012 07:00:00 am             3
C2 E1 11/01/2012 07:00:00 am - 11/01/2012 07:30:00 am            30
C2 E1 11/01/2012 07:30:00 am - 11/01/2012 08:00:00 am            30
C2 E1 11/01/2012 08:00:00 am - 11/01/2012 08:30:00 am             1

11 rows selected.
partition by(id)
dimension by(0 as f)
for f from 0 to blocks[0]-1  increment 1
EM START_TIME             END_TIME               CO START_BLOCK                BLOCKS
-- ---------------------- ---------------------- -- ---------------------- ----------
E1 11/01/2012 06:55:00 am 11/01/2012 07:01:00 am C1 11/01/2012 06:30:00 am          2
start_range[0 to 1] = 11/01/2012 06:30:00 am + (30minutes * the value of f)
start_range[0] = 11/01/2012 06:30:00 am + (30min*0) = 11/01/2012 06:30:00 am
start_range[1] = 11/01/2012 06:30:00 am + (30min*1) = 11/01/2012 07:00:00 am
end_range[any]  =  start_range[cv()] + (30/1440),
range [any] = to_char(start_range[cv()], 'dd/mm/yyyy hh:mi:ss am') || ' - ' || to_char(end_range[cv()], 'dd/mm/yyyy hh24:mi:ss am'),
minutes [any]   = case
                    when start_time[0] between start_range[cv()] and end_range[cv()]
                    then 1440 *(end_range[cv()] - start_time[0])
                    when end_time[0] between start_range[cv()] and end_range[cv()]
                    then 1440 *(end_time[0] - start_range[cv()])
                    else 1440 * (end_range[cv()] - start_range[cv()])
                  end );
SQL> with foo as (select rownum id, emp, start_time, end_time, code,
  2                      trunc(start_time, 'mi')
  3                      - (mod(to_char(trunc(start_time, 'mi'), 'mi'), 30) / 1440) start_block,
  4                      ceil(2*24*(end_time-(trunc(start_time, 'mi')
  5                      - (mod(to_char(trunc(start_time, 'mi'), 'mi'), 30) / 1440)))) blocks
  6                 from tab f)
  7  select thedate, code, range, sum(minutes) minutes
  8    from (select trunc(start_time) thedate, code, emp, range, minutes
  9            from foo
 10           model partition by(id)
 11                 dimension by(0 as f)
 12                 measures(code, emp, start_time, end_time, start_block, blocks,
 13                          sysdate as start_range,
 14                          sysdate as end_range,
 15                          cast(0 as number) minutes,
 16                          cast('' as varchar2(50)) range)
 17                 rules (start_range [for f from 0 to blocks[0]-1  increment 1]  = start_block[0] + (30*cv(f)/1440),
 18                        code[any]  =  code[0],
 19                        emp[any]  =  emp[0],
 20                        end_range[any]  =  start_range[cv()] + (30/1440),
 21                        start_time[any]  =  start_time[0],
 22                        end_time[any]  =  end_time[0],
 23                        range [any] = to_char(start_range[cv()], 'dd/mm/yyyy hh:mi:ss am') || ' - ' || to_char(end_range[cv()], 'dd/mm/yyyy hh24:mi:ss am'),
 24                        minutes [any]   = case
 25                                            when start_time[0] between start_range[cv()] and end_range[cv()]
 26                                            then 1440 *(end_range[cv()] - start_time[0])
 27                                            when end_time[0] between start_range[cv()] and end_range[cv()]
 28                                            then 1440 *(end_time[0] - start_range[cv()])
 29                                            else 1440 * (end_range[cv()] - start_range[cv()])
 30                                          end ))
 31   group by thedate, code, range
 32   order by thedate, code, range;

THEDATE    CO RANGE                                                 MINUTES
---------- -- -------------------------------------------------- ----------
11/01/2012 C1 11/01/2012 06:30:00 am - 11/01/2012 07:00:00 am             5
11/01/2012 C1 11/01/2012 07:00:00 am - 11/01/2012 07:30:00 am             1
11/01/2012 C2 11/01/2012 06:30:00 am - 11/01/2012 07:00:00 am             6
11/01/2012 C2 11/01/2012 07:00:00 am - 11/01/2012 07:30:00 am            60
11/01/2012 C2 11/01/2012 07:30:00 am - 11/01/2012 08:00:00 am            60
11/01/2012 C2 11/01/2012 08:00:00 am - 11/01/2012 08:30:00 am             1
11/02/2012 C1 11/02/2012 07:30:00 am - 11/02/2012 08:00:00 am            25
11/02/2012 C1 11/02/2012 08:00:00 am - 11/02/2012 08:30:00 am             1
  with v_data as (
    select 1 pk, 'E1' emp, to_date('2012-11-01', 'YYYY-MM-DD') as date1, to_date('2012-11-01 06:55:00', 'YYYY-MM-DD hh24:mi:ss') as start_time, to_date('2012-11-01 07:01:00', 'YYYY-MM-DD hh24:mi:ss') as end_time, 'C1' as code, 6 as minutes from dual union all 
    select 2 pk, 'E1' emp, to_date('2012-11-01', 'YYYY-MM-DD') as date1, to_date('2012-11-01 06:57:00', 'YYYY-MM-DD hh24:mi:ss') as start_time, to_date('2012-11-01 08:01:00', 'YYYY-MM-DD hh24:mi:ss') as end_time, 'C2' as code, 64 as minutes from dual union all 
    select 3 pk, 'E2' emp, to_date('2012-11-01', 'YYYY-MM-DD') as date1, to_date('2012-11-01 06:57:00', 'YYYY-MM-DD hh24:mi:ss') as start_time, to_date('2012-11-01 08:00:00', 'YYYY-MM-DD hh24:mi:ss') as end_time, 'C2' as code, 63 as minutes from dual union all 
    select 4 pk, 'E1' emp, to_date('2012-11-02', 'YYYY-MM-DD') as date1, to_date('2012-11-02 07:35:00', 'YYYY-MM-DD hh24:mi:ss') as start_time, to_date('2012-11-02 08:01:00', 'YYYY-MM-DD hh24:mi:ss') as end_time, 'C1' as code, 26 as minutes from dual), 
v_buckets as (          
  select 
    to_date('2012-11-01', 'YYYY-MM-DD') + (rownum-1)/48 as bucket_start,
    to_date('2012-11-01', 'YYYY-MM-DD') + rownum/48 as bucket_end  
    from dual
  connect by rownum <96
)
select v3.date1, v3.bucket_start, v3.bucket_end, v3.code, sum(v3.time_spent_in_bucket) as minutes 
from (
select v2.*, (least(end_time, bucket_end) - greatest(start_time, bucket_start))*1440 as time_spent_in_bucket from 
(
select buck.*,
       v1.*
  from v_buckets buck
  join v_data v1 
    on (
       -- time slot completely contained in one bucket
        (v1.start_time >= buck.bucket_start and v1.start_time < buck.bucket_end and
        v1.end_time >= buck.bucket_start and v1.end_time < buck.bucket_end)
       -- time slot starts in bucket, expands to next bucket
        or (v1.start_time >= buck.bucket_start and v1.start_time < buck.bucket_end and
        v1.end_time >= buck.bucket_end)
       -- time slot started in previous bucket, ends in this bucket)
        or (v1.start_time < buck.bucket_start and v1.end_time > buck.bucket_start and
        v1.end_time <= buck.bucket_end)
       -- time slot began in previous bucket, expands to next bucket
       or (v1.start_time < buck.bucket_start and v1.end_time >= buck.bucket_end) 
        )
) v2
) v3 
where start_time is not null
group by date1, bucket_start, bucket_end, code
order by bucket_start, code
select trunc(trunc_start) as datetime, code, range , sum(duration) minutes
from (
select code, end_time, start_time, TRUNC_START , 
  to_char(trunc_start,'hh:mi:ss AM')||'-'||to_char(trunc_start+1/48,'hh:mi:ss AM') as range,
  case 
    when end_time-trunc_start between 0 and 1/48 then (end_time-trunc_start)*1440 
    when start_time-trunc_start between 0 and 1/48 then (trunc_start-start_time)*1440+30
    else 30 
  end as duration
from(
  select  s.*, n , 
  trunc(start_time) + trunc((start_time-trunc(start_time))*48)/48 + (n-1)/48 as trunc_start
  from s
  join (select level n from dual connect by level <=48) a
  on n-2 <= (end_time-start_time)*100
  )b
)
where trunc_start < end_time --eliminating fake intervals
group by code, trunc(trunc_start), range
order by 1, 3
;