Avatar billede sorenaakjaer Nybegynder
22. januar 2013 - 16:12 Der er 10 kommentarer

Dagens udfordring - Beregning af arbejdstid

Hej,

Jeg har behov for en funktion til at beregne antal arbejds minutter mellem to timestamps. Arbejdstiden styres via en tabel som indeholder timestamps for hvornår arbejdstiden starter og slutter.

Jeg har ingen ide om hvordan jeg skal beregne dette.

Er det en af jer kloge hoveder som kan hjælpe med en sådan funktion som baserer sig på start og slut timestamps fra en anden tabel.

Eksempel:
Start tid: 22-01-2013 09:00:13
Slut tid: 23-01-2013 09:00:13

Tabel med åbningstid: WF_WORK_CALENDAR
Starttid                  Sluttid
22-01-2013 08:00:00        22-01-2013 16:00:00
23-01-2013 08:00:00        23-01-2013 16:00:00

Ønsket resultat: 480 minutter (Altså 6 timer, 59 min og 47 sek d. 22-01 og 1 time og 13 sek d. 23-01-2013
Avatar billede Slettet bruger
22. januar 2013 - 16:25 #1
Avatar billede arne_v Ekspert
22. januar 2013 - 17:14 #2
peterolsen>

Jeg tror ikke helt at du har forstaaet problemstillingen.
Avatar billede arne_v Ekspert
22. januar 2013 - 17:18 #3
Jeg tror ikke at SQL er godt til dette.

Derfor:
* hvis muligt flyt beregningen fra databasen til applikationen
* hvis ikke muligt saa lave en UDF i Java

Simple algoritme:

laes fra arbejdstids tabel ind i array/collection af klasse med start of slut

antal minutter = 0

for hvert minut mellem start og slut{
    for hver arbejdsperiode {
        hvis tid i periode {
            antal minutter ++
        }
    }
}
Avatar billede arne_v Ekspert
22. januar 2013 - 17:20 #4
Hvis performance er vigtig, saa kan algoritmen forbedres en hel del.

Man kan bl.a.:
- teste om en hel timer er der og saa +60
- undlade at loebe igennem alle arbejds perioder

Men der bliver hurtigt en del if saetninger i den logik.
Avatar billede sorenaakjaer Nybegynder
22. januar 2013 - 18:58 #5
Hej,

Jeg prøver lige at uddybe lidt.

Jeg har en tabel på en database indeholdende to timestamps vedr. hvornår en sag er modtaget (timestamp 1) og til hvornår en sag er behandlet (timestamp 2). Disse timestamps ønsker jeg at beregne antal mellem disse timestamps indenfor arbejdstid.

Min problemstilling opstår når delta kun skal udregnes indenfor arbejdstid som er defineret i tabellen WF_WORK_CALENDAR

Tabellen WF_WORK_CALENDAR har følgende kolonner. Som det ses så er åbningstiden forskellig dag for dag.

Starttid                  Sluttid
22-01-2013 08:00:00        22-01-2013 16:00:00
23-01-2013 08:00:00        23-01-2013 16:00:00
24-01-2013 08:00:00        24-01-2013 16:00:00
25-01-2013 08:00:00        25-01-2013 15:30:00
25-01-2013 10:00:00        25-01-2013 16:00:00
25-01-2013 00:00:00        25-01-2013 00:00:00

Jeg vil gerne, at beregningen foretages i en database funktion som jeg kan genanvende.

Jeg har fundet følgende funktion som inspiration, men denne kigger dog ikke på tabel som udgangspunkt for åbningstiderne.

https://forums.oracle.com/forums/thread.jspa?messageID=10658241

CREATE OR REPLACE
  FUNCTION get_bus_minutes_between(
                                  p_start_date DATE,
                                  p_end_date DATE
                                  )
    RETURN NUMBER
    IS
        v_return NUMBER;
    BEGIN
        with t as (
                  select  case level
                            when 1 then greatest(p_start_date,trunc(p_start_date) + 9 / 24)
                            else trunc(p_start_date) + level - 15 / 24
                          end start_dt,
                          case connect_by_isleaf
                            when 1 then least(p_end_date,trunc(p_end_date) + 18 / 24)
                            else trunc(p_start_date) + level - 8 / 24
                          end end_dt
                    from  dual
                    connect by level <= trunc(p_end_date) - trunc(p_start_date) + 1
                  )
        select  sum(greatest(end_dt - start_dt,0)) * 24 * 60 work_minutes
          into  v_return
          from  t
          where trunc(start_dt) - trunc(start_dt,'iw') < 5; -- exclude weekends
        RETURN v_return;
END;
Avatar billede sorenaakjaer Nybegynder
22. januar 2013 - 19:08 #6
Hej,

Jeg prøver lige at uddybe lidt.

Jeg har en tabel på en database indeholdende to timestamps vedr. hvornår en sag er modtaget (timestamp 1) og til hvornår en sag er behandlet (timestamp 2). Disse timestamps ønsker jeg at beregne antal mellem disse timestamps indenfor arbejdstid.

Min problemstilling opstår når delta kun skal udregnes indenfor arbejdstid som er defineret i tabellen WF_WORK_CALENDAR

Tabellen WF_WORK_CALENDAR har følgende kolonner. Som det ses så er åbningstiden forskellig dag for dag.

Starttid                  Sluttid
22-01-2013 08:00:00        22-01-2013 16:00:00
23-01-2013 08:00:00        23-01-2013 16:00:00
24-01-2013 08:00:00        24-01-2013 16:00:00
25-01-2013 08:00:00        25-01-2013 15:30:00
25-01-2013 10:00:00        25-01-2013 16:00:00
25-01-2013 00:00:00        25-01-2013 00:00:00

Jeg vil gerne, at beregningen foretages i en database funktion som jeg kan genanvende.

Jeg har fundet følgende funktion som inspiration, men denne kigger dog ikke på tabel som udgangspunkt for åbningstiderne.

https://forums.oracle.com/ (...)

CREATE OR REPLACE
  FUNCTION get_bus_minutes_between(
                                  p_start_date DATE,
                                  p_end_date DATE
                                  )
    RETURN NUMBER
    IS
        v_return NUMBER;
    BEGIN
        with t as (
                  select  case level
                            when 1 then greatest(p_start_date,trunc(p_start_date) + 9 / 24)
                            else trunc(p_start_date) + level - 15 / 24
                          end start_dt,
                          case connect_by_isleaf
                            when 1 then least(p_end_date,trunc(p_end_date) + 18 / 24)
                            else trunc(p_start_date) + level - 8 / 24
                          end end_dt
                    from  dual
                    connect by level <= trunc(p_end_date) - trunc(p_start_date) + 1
                  )
        select  sum(greatest(end_dt - start_dt,0)) * 24 * 60 work_minutes
          into  v_return
          from  t
          where trunc(start_dt) - trunc(start_dt,'iw') < 5; -- exclude weekends
        RETURN v_return;
END;
Avatar billede Slettet bruger
22. januar 2013 - 21:08 #7
Her er et forsøg på SQL:

select (least(A.SlutTid,C.SlutTid)
        -greatest(A.StartTid,C.StartTid)
        )*24*60 Minutter
from Arbejdstider A, WF_WORK_CALENDAR C
where C.SlutTid > A.StartTid
  and C.StartTid < A.SlutTid
Avatar billede sorenaakjaer Nybegynder
24. januar 2013 - 08:32 #8
Den virker desværre ikke helt...

Jeg har fundet en anden funktion som højst sandsynligt bare skal tilpasses.

Nedenstående SQL funktion returnerer tidspunkt hvor hvor jeg har et starttidpunkt og + antal arbejdsminutter.


CREATE OR REPLACE FUNCTION WF_CalcWorkDay(dStartDate date, nWorkMinutes number) return date is
  wdStart date;
  wdEnd date;
  dEndDate date;
  nLeftMinutes number;
BEGIN
if nWorkMinutes is null then
  return(null);
end if;

dEndDate := dStartDate;
nLeftMinutes := nWorkMinutes;

while nLeftMinutes >= 0 loop
BEGIN
  select startdate, enddate
    into wdStart, wdEnd
    from wf_work_calendar
  where to_char(xdate, 'YYYY-MM-DD') = to_char(dEndDate, 'YYYY-MM-DD');
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    return(null);
END;
  if dStartDate > wdStart then
  /* Add part of the first workday */
  wdStart := dStartDate;
  end if;

  if 24*60*(wdEnd - wdStart) <= nLeftMinutes then
  /* Add a full workday */
  dEndDate := to_date(to_char(dEndDate, 'YYYY-MM-DD'), 'YYYY-MM-DD') + 1;
  if wdEnd - wdStart > 0 then
      nLeftMinutes := nLeftMinutes - 24*60*(wdEnd - wdStart);
  end if;
  else
  /* Add the part of the last workday */
  dEndDate := wdStart + nLeftMinutes/24/60;
  nLeftMinutes := -1;
  end if;
end loop;

  return(dEndDate);
END;
Avatar billede Slettet bruger
24. januar 2013 - 09:11 #9
Virker ikke helt - virker det lidt?

I dine beskrivelse af åbningstider har du tider der lapper over hinanden den 25/1-13 - er det korrekt?

25-01-2013 08:00:00        25-01-2013 15:30:00
25-01-2013 10:00:00        25-01-2013 16:00:00
Avatar billede Slettet bruger
24. januar 2013 - 09:31 #10
Jeg har efterprøvet mit SQL og det virker fint hvis der ikke er overlappende åbningstider. Så er problemet her?
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Computerworld tilbyder specialiserede kurser i database-management

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester