简体   繁体   中英

Select sum of hours grouped by a day first and then by a week

in Oracle ApEx I have a table like this:

CREATE TABLE  "ATTENDANCE_HOURS" 
   ("ID" NUMBER NOT NULL ENABLE, 
    "PERSON_ID" NUMBER, 
    "PROJECT_ID" NUMBER, 
    "FROM_X" DATE NOT NULL ENABLE, 
    "TO_X" DATE NOT NULL ENABLE, 
    "NOTE" VARCHAR2(300), 
    "APPROVED" NUMBER DEFAULT 0 NOT NULL ENABLE, 
    "APPROVAL_NOTE" VARCHAR2(300), 
    "DAY" DATE NOT NULL ENABLE, 
     CONSTRAINT "CHECK_TIMES" CHECK (TO_CHAR(TO_X, 'HH24MI') > TO_CHAR(FROM_X, 'HH24MI')) ENABLE, 
 CONSTRAINT "ATTENDANCE_HOURS_PK" PRIMARY KEY ("ID")
  USING INDEX  ENABLE
   )

Note: I cannot change the table. Just nope. I need to, for each person, select how many hours a week they worked. I already have a selection for How many hours a day (thanks StackOverflow) that looks like this:

select 
    (MAX("TO_X") - MIN("FROM_X"))*24 - 
    (max(case when PROJECT_ID = 21 then to_x else to_date('01012000','DDMMYYYY') end) -
    max(case when PROJECT_ID = 21 then from_x else to_date('01012000','DDMMYYYY')       end))*24 AS TIME_SPENT
// project id = 21 is break
from #OWNER#.ATTENDANCE_HOURS
GROUP BY DAY

What occured to me, was to group the entries next by week number and put the whole thing into SUM(), but well, it doesn't work and it says that: not a single-group group function. Or maybe I should start playing with views?

Total hours for each person in a week would look something like this:

select person_id, to_char(day, 'YYYY-WW') as week,
       sum(to_x - from_x) as hours_worked
from #OWNER#.ATTENDANCE_HOURS
group by person_id, to_char(day, 'YYYY-WW')
order by person_id, week;

I have no idea what the extra logic is on the project id. Your question doesn't explain it, so I removed it as unnecessary for calculating the sum of the hours. You can add it back in, if it is important.

Something like this (untested)?

select person_id
     , trunc(from_x,'iw')                                     first_day_of_iso_week
     , sum((to_x - from_x) * 24)                              all_hours
     , sum((case project_id when 21 then to_x - from_x) * 24) break_hours
  from attendance_hours
 group by person_id        -- for each person
     , trunc(from_x,'iw')  -- for each ISO-week

Maybe you need an extra check constraint telling that the date part of to_x and from_x should be the same, check(trunc(from_x) = trunc(to_x))?

EDIT: I now see you have a superflous DAY column in your table as well. That's not a good idea as you now have to check (trunc(day) = trunc(to_x)) and trunc(day) = trunc(from_x). If you can, get rid of the day column: it is already included in your from_x and to_x columns.

The following query gives you the sum of worked hours minus breaks for each person (provided project 21 means break and all others mean work):

select person_id, 
  sum(case when project_id = 21 then -1 else 1 end * (to_x - from_x)) * 24
from #OWNER#.ATTENDANCE_HOURS
group by person_id;

To get this per day can be quite complicated when work can be over midnight (or even span more than two days). As long as we assume that from_x and to_x are aways the same day, we get:

select person_id, trunc(from_x),
  sum(case when project_id = 21 then -1 else 1 end * (to_x - from_x)) * 24
from #OWNER#.ATTENDANCE_HOURS
group by person_id, trunc(from_x);

To get this per week would then be:

select person_id, trunc(from_x, 'IW'),
  sum(case when project_id = 21 then -1 else 1 end * (to_x - from_x)) * 24
from #OWNER#.ATTENDANCE_HOURS
group by person_id, trunc(from_x, 'IW');

EDIT: Oh, I just notice that I don't know enough of your data. Can a project attendance range from 9am to 11am when there is also a break from 10am to 10:15am? Then my queries work. Or would that be two project attendances then, one ranging from 9am to 10am and the other from 10:15am to 11am? Then you would need sum(case when project_id = 21 then 0 else to_x - from_x end) * 24 instead.

EDIT: Okay, as you say in your comment that project_id = 21 cannot overlap, simply exclude it from your query:

select person_id, trunc(from_x, 'IW'), sum(to_x - from_x) * 24
from #OWNER#.ATTENDANCE_HOURS
where project_id <> 21 
group by person_id, trunc(from_x, 'IW');

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM