01. september 2005 - 23:53Der er
33 kommentarer og 1 løsning
Tidsberegninger udfra input i en form.
Hejsa folkens.
Jeg har følgende problem/udfordring :
Jeg har en online vagtplan, som egentlig fungerer udemærket. Alle entries står som en "Varchar" data type i min MS SQL database.
Forestil jer, at der lægger værdier hvor 3 forskellige personer (medarbejdere) i databasen. En værdi for hver dag ( Mandag til Søndag). Der ligger altså en entry for hver person, som er på arbejde f.eks. Mandag. Endnu en entry for hver person som er på arbejde Tirsdag osv.
Jeg bruger følgende colums : ID, BRUGERNAVN, STARTTID, SLUTTID, UGEDAG, UGE og VAGTTYPE.
Jeg kunne godt tænke mig at kunne lægge alle værdier sammen for en medarbejder. Så jeg på en side kan trække data ud fra min database, og udskrive på skærmen hvor mange timer en person er på arbejde i løbet af en uge.
Er starttid og sluttid en hel dato eller er det bare et tidspunkt. Hvis det er et tidspunkt bliver det mere kompliceret at trække data ud.
Anyway! Første hug kunne være:
SELECT SUM(SLUTTID - STARTTID) AS TID, BRUGERNAVN FROM <tabel med vagttider> WHERE UGE = '<det ugenr du vil have timer for>' AND BRUGERNAVN = '<den bruger du vil have tidsforbrug for>'
eller dem allesammen
SELECT SUM(SLUTTID - STARTTID) AS TID, BRUGERNAVN FROM <tabel med vagttider> WHERE UGE = '<det ugenr du vil have timer for>' GROUP BY BRUGERNAVN
Ja, jeg har siddet og kigget lidt på det nu. Jeg har lavet følgende kode :
<% sum = SLUTTID - STARTTID %> <%=sum%>
Det virker også fint, men problemet er bare den måde mine data står på.
F.eks. hvis STARTTID er kl. 08 om morgenen, så står der : 08.00 Og er SLUTTID 16, så står der i min DB : 16.00
Når jeg trækker de to tal fra hinanden, så laver systemet følgende regne stykke : 1600 - 0800 = 800 Timer. Det er jo forkert. Kan man ikke formatere på en måde, så man kan angive at de første 2 tal er timer og de sidste 2 er min.
Så ville jeg vel kunne få den til at regne rigtigt ud? Eller????
OK! Her er lige et sindsygt (utestet!) forsøg på at klare det hele på SQL serveren:
SELECT SUM(SLUTTID - STARTTID) AS TIDSFORBRUG, BRUGERNAVN FROM (SELECT ((CAST(STARTTID AS INT)/100)*100)+(CAST(STARTTID AS INT)%100*1.66666) AS STARTTID1, (((CAST(SLUTTID AS INT)/100)*100)+(CAST(SLUTTID AS INT)%100*1.66666))+2400 AS SLUTTID1, BRUGERNAVN FROM <tabel> WHERE STARTTID > SLUTTID UNION SELECT ((CAST(STARTTID AS INT)/100)*100)+(CAST(STARTTID AS INT)%100*1.66666) AS STARTTID1, (((CAST(SLUTTID AS INT)/100)*100)+(CAST(SLUTTID AS INT)%100*1.66666)) AS SLUTTID1, BRUGERNAVN FROM <tabel> WHERE STARTTID <= SLUTTID) WHERE UGE = <det ugenr du vil have timer for> GROUP BY BRUGERNAVN
Så skal du bare trække tidsforbrug og brugernavn ud af resultatet...
Typisk! Så får man lige sneget en fejl ind! Kolonnenavnene har jo ændret sig i subquery, de hedder naturligvis STARTTID1 og SLUTTID1:
SELECT SUM(SLUTTID1 - STARTTID1) AS TIDSFORBRUG, BRUGERNAVN FROM (SELECT ((CAST(STARTTID AS INT)/100)*100)+(CAST(STARTTID AS INT)%100*1.66666) AS STARTTID1, (((CAST(SLUTTID AS INT)/100)*100)+(CAST(SLUTTID AS INT)%100*1.66666))+2400 AS SLUTTID1, BRUGERNAVN FROM <tabel> WHERE STARTTID > SLUTTID UNION SELECT ((CAST(STARTTID AS INT)/100)*100)+(CAST(STARTTID AS INT)%100*1.66666) AS STARTTID1, (((CAST(SLUTTID AS INT)/100)*100)+(CAST(SLUTTID AS INT)%100*1.66666)) AS SLUTTID1, BRUGERNAVN FROM <tabel> WHERE STARTTID <= SLUTTID) WHERE UGE = <det ugenr du vil have timer for> GROUP BY BRUGERNAVN
Okay, det var godt nok en vild SQL sætning. Jeg forstår ikke halvdelen af den, men har lige prøvet.
Jeg har rettet lidt i kollone navnene, så de passer til hvad jeg faktisk havde kaldt dem. (Mig der skrev forkert i første indlæg)...
Det virker ikke helt.
SQL jeg har fyret af i min SQL Manager er :
SELECT SUM(SLUTTID1 - STARTTID1) AS TIDSFORBRUG, BRUGERNAVN FROM (SELECT ((CAST(STARTTIME AS INT)/100)*100)+(CAST(STARTTIME AS INT)%100*1.66666) AS STARTTID1, (((CAST(ENDTIME AS INT)/100)*100)+(CAST(ENDTIME AS INT)%100*1.66666))+2400 AS SLUTTID1, BRUGERNAVN FROM koldingvagtplan WHERE STARTTIME > ENDTIME UNION SELECT ((CAST(STARTTIME AS INT)/100)*100)+(CAST(STARTTIME AS INT)%100*1.66666) AS STARTTID1, (((CAST(ENDTIME AS INT)/100)*100)+(CAST(ENDTIME AS INT)%100*1.66666)) AS SLUTTID1, NAME FROM koldingvagtplan WHERE STARTTIME <= ENDTIME) WHERE WEEK = '40' GROUP BY BRUGERNAVN
Den giver følgende fejl :
Msg 156, Level 15, State 1, Line 14 Incorrect syntax near the keyword 'WHERE'.
hmmm... jeg tror nok denne fungerer (godt nok stadig med de gamle kolonnenavne, men dem kan du selv rette igen):
SELECT SUM(CAST(REPLACE(SLUTTID1,'.','') AS INT) - CAST(REPLACE(STARTTID1,'.','') AS INT)) AS TIDSFORBRUG, BRUGERNAVN, FROM ((SELECT (CAST(REPLACE(STARTTID,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTID,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, ((CAST(REPLACE(SLUTTID,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(SLUTTID,'.','') AS INT)%100*1.66666),0) AS INT)+2400) AS SLUTTID1, BRUGERNAVN, UGE FROM vagtplan WHERE STARTTID > SLUTTID AND UGE = '5') UNION (SELECT (CAST(REPLACE(STARTTID,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTID,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, (CAST(REPLACE(SLUTTID,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(SLUTTID,'.','') AS INT)%100*1.66666),0) AS INT) AS SLUTTID1, BRUGERNAVN, UGE FROM vagtplan WHERE STARTTID <= SLUTTID AND UGE = '5')) v1 GROUP BY BRUGERNAVN
I bund og grund handler en stor del af den SQL om at konvertere dine tidspunkt og håndtere overskridelse af midnat (den kan dog ikke håndtere arbejdsdage længere end 24 timer!!!).
Mht. konvertering af tidspunkter, så sker der det at punktum fjernes og tallet konverteres til et heltal. Derefter dividerer jeg med 100 og ganger med 100 for at fjerne minutter. Til de hele timer lægger jeg så resten af divisionen (modolus) med 100 (dvs. minutterne), hvilken jeg ganger med 1.666 for at få minutterne regnet om til 1/100 i stedet for 1/60.
Jeg laver to resultater som jeg samler til et langt. Det første lægger 24 timer til sluttiden hvis den er mindre end starttiden (dem hvor vagten har krydset midnat). Det andet er dem hvor starttid er mindre eller lig med sluttid.
Ud af det summerer jeg så differencen mellem de to tider (som nu altid vil give et positivt tal!) og grupperer efter brugernavn, så den forbrugte tid er for den pågældende bruger. Dvs. alle brugere burde komme ud med deres forbrugte tid i den pågældende uge (i mit eksempel altså uge 5).
HUSK! at rette uge i begge subqueries, ellers får du nok nogle sære resultater :)
Jeg skal vist til at læse lidt op på de funktioner en SQL Server tilbyder.
Jeg vil helst ikke begynde at rode alt for meget rundt på diverse tegn, da jeg slet ikke er sikker på hvorfor de står som de gør.
Jeg har nu forsøgt mig igen. Dog stadig med fejl. Gør jeg noget forkert??? : Her er koden som jeg bruger den :
--------------
SELECT SUM(CAST(REPLACE(SLUTTID1,'.','') AS INT) - CAST(REPLACE(STARTTID1,'.','') AS INT)) AS TIDSFORBRUG, NAME, FROM KOLDINGVAGTPLAN ((SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, ((CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT)+2400) AS SLUTTID1, NAME, WEEK FROM KOLDINGVAGTPLAN WHERE STARTTIME > ENDTIME AND UGE = '5') UNION (SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, (CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS SLUTTID1, NAME, WEEK FROM KOLDINGVAGTPLAN WHERE STARTTIME <= ENDTIME AND WEEK = '40')) v1 GROUP BY NAME
-----------
Fejl jeg får er følgende :
Msg 156, Level 15, State 1, Line 4 Incorrect syntax near the keyword 'FROM'. Msg 170, Level 15, State 1, Line 14 Line 14: Incorrect syntax near 'v1'.
SELECT SUM(CAST(REPLACE(SLUTTID1,'.','') AS INT) - CAST(REPLACE(STARTTID1,'.','') AS INT)) AS TIDSFORBRUG, NAME FROM ((SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, ((CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT)+2400) AS SLUTTID1, NAME, WEEK FROM KOLDINGVAGTPLAN WHERE STARTTIME > ENDTIME AND WEEK = '40') UNION (SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, (CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS SLUTTID1, NAME, WEEK FROM KOLDINGVAGTPLAN WHERE STARTTIME <= ENDTIME AND WEEK = '40')) v1 GROUP BY NAME
Umiddebart ville jeg jo synes at et resultat der skriver 2,5 timer ville være mere normalt, nå man snakker timeforbrug. i det tilfælde behøver vi bare at dividere tidsforbrug med 100. Hvis du kan leve med det, så skal din SQL ændres en lille smule:
SELECT CAST(SUM(CAST(REPLACE(SLUTTID1,'.','') AS INT) - CAST(REPLACE(STARTTID1,'.','') AS INT)) AS TIDSFORBRUG, NAME FROM ((SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, ((CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT)+2400) AS SLUTTID1, NAME, WEEK FROM KOLDINGVAGTPLAN WHERE STARTTIME > ENDTIME AND WEEK = '40') UNION (SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, (CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS SLUTTID1, NAME, WEEK FROM KOLDINGVAGTPLAN WHERE STARTTIME <= ENDTIME AND WEEK = '40')) v1 GROUP BY NAME
SELECT CAST(SUM(CAST(REPLACE(SLUTTID1,'.','') AS INT) - CAST(REPLACE(STARTTID1,'.','') AS INT)) AS FLOAT)/100 AS TIDSFORBRUG, NAME FROM ((SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, ((CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT)+2400) AS SLUTTID1, NAME, WEEK FROM KOLDINGVAGTPLAN WHERE STARTTIME > ENDTIME AND WEEK = '40') UNION (SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, (CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS SLUTTID1, NAME, WEEK FROM KOLDINGVAGTPLAN WHERE STARTTIME <= ENDTIME AND WEEK = '40')) v1 GROUP BY NAME
Men lige en lille ting til. Ved du hvad man skal skrive i ASP for at kunne holde formatteringen af dit SQL script? Altså Linie skift osv. Ellers skal det hele jo stå på en linie og det vil jeg helst ikke!
Skal man ikke skrive noget i slutningen af hver linie???
Tænker du på formatteringen af selve SQL-sætningen? Hvis ja, så kan du bruge underscore tegnet. Således:
SQL = "SELECT " & _ "CAST(SUM(CAST(REPLACE(SLUTTID1,'.','') AS INT) - CAST(REPLACE(STARTTID1,'.','') AS INT)) AS FLOAT)/100 AS TIDSFORBRUG, " & _ "NAME " & _ "FROM " & _ "((SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, " & _ "((CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT)+2400) AS SLUTTID1, " & _ "NAME, " & _ "WEEK " & _ "FROM KOLDINGVAGTPLAN " & _ "WHERE STARTTIME > ENDTIME " & _ "AND WEEK = '40') " & _ "UNION " & _ "(SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, " & _ "(CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS SLUTTID1, " & _ "NAME, " & _ "WEEK " & _ "FROM KOLDINGVAGTPLAN " & _ "WHERE STARTTIME <= ENDTIME " & _ "AND WEEK = '40')) v1 " & _ "GROUP BY " & _ "NAME "
Vær dog opmærksom på at der nok er en begrænsning i hvormange linier du skal strække et statement over.
Grundlæggende vil jeg faktisk anbefale at du laver dette statement som en stored procedure, hvilket også vil have andre fordele (f.eks. hastighed, pga. SQL Servers mulighed for at planlægge forspørgslen, samt at den er oversat når den ligger som en SP).
Nu har jeg selvfølgelig ikke fået formatteret SQL sætningen, så den står på en linie endnu.
Det virker rigtig godt, men der er en ting som irriterer mig.
F.eks :
En medarbejder er på arbejde både Mandag, Tirsdag og Onsdag. Medarbejderen møder :
Mandag : kl. 14.00 og går hjem kl. 16.00. Tirsdag : kl. 13.00 og går hjem kl. 17.00.
Her tæller dit SQL Script fint sammen. RS("TIDSFORBRUG") bliver her udskrevet korrekt, altså : 6 Timer
Men hvis 2 dage er ens, lægger den ikke den sidste dag til.
F.eks. : Medarbejderen møder :
Mandag : kl. 14.00 og går hjem kl. 16.00. Tirsdag : kl. 14.00 og går hjem kl. 16.00.
I dette tilfælde bliver rs("TIDSFORBRUG") sat til : 2 Jeg har også en kollone der hedder WEEKDAY. Jeg forstår ikke SQL sætningen godt nok til at jeg selv vil begynde at pille ved den, men kan man ikke bygge den kollone ind i også? Så vil det jo aldrig blive 2 ens entries.
Først og fremmest forstår jeg ikke helt hvorfor det skulle være et problem at man to dage møder og går hjem på samme tidspunkt - du ændrer vel ikke noget i den måde du registrerer data på i det tilfælde...(?) Databasen betragter det bare som to rækker der skal summeres efter de angivne regler (og jeg kan ikke lige selv gennemskue hvor det skulle gå galt med start og sluttider på tværs af to dage).
Grunden til at weekday ikke kommer med er at forspørgslen kun finder tidsforbrug pr. ugenr. hvilket ikke lægger op til at de enkelte ugedage skal bruges til noget.
Kan du evt. vise et udsnit af relevante data som resulterer i fejlagtigt udtræk?
Jo, men for at beholde min stolthed, vil jeg lige rydde lidt op i min kode. Den er MEGET rodet, og det er vist kun mig selv der kan finde hoved og hale i den! Har du evt. en mail adresse hvor jeg kan sende dig den på?
OK! Så lærte jeg lige lidt mere om UNION. UNION tager åbenbart og samler enslydende rækker til en, hvilket gjorde sig gældende for to af rækkerne lørdag og søndag. Det man skal gøre er at skrive UNION ALL som resulterer i at alle rækker returneres selvom der måtte være sammenfald i data. Altså:
SELECT CAST(SUM(SLUTTID1 - STARTTID1) AS FLOAT)/100 AS TIDSFORBRUG, NAME FROM ((SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, ((CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT)+2400) AS SLUTTID1, NAME, WEEK FROM KOLDINGVAGTPLAN WHERE STARTTIME > ENDTIME AND WEEK = '40') UNION ALL (SELECT (CAST(REPLACE(STARTTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(STARTTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS STARTTID1, (CAST(REPLACE(ENDTIME,'.','') AS INT)/100*100)+CAST(ROUND((CAST(REPLACE(ENDTIME,'.','') AS INT)%100*1.66666),0) AS INT) AS SLUTTID1, NAME, WEEK FROM KOLDINGVAGTPLAN WHERE STARTTIME <= ENDTIME AND WEEK = '40')) v1 GROUP BY NAME
Synes godt om
Ny brugerNybegynder
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.