Avatar billede eksperten1 Nybegynder
01. september 2008 - 15:09 Der er 12 kommentarer og
1 løsning

Avanceret inner join - udregn time_diff imellem records

Jeg har en tabel, hvor i der findes en række events/records. Jeg ønsker at lave et udtræk som giver mig tidsforskellen imelle første og sidste event for en event.

stat
------
id (int)
unikey (varchar(50))
dato (date)
tid (time)
rutine (tinyint)

(det er de væsentlige info - der er en masse uinteressante felter som beskriver eventen)

OK - de her event beskriver en fysisk handling hvor en medarbejder scanner en pakke i virksomheden.
Et scan kan i feltet rutine enten være
1 : Modtaget
2 : Leveret
3 : Terminal (bruges ikke her)

Jeg ønsker at lave et udtræk hvor jeg finder unikke UNIKEY og udregner tidsforskellen i timer fra FØRSTE (rutine=1) til SIDSTE (rutine=2) - der kan være (i teorien uendeligt) en masse "mellem-scan" som kan have både 1,2,3

Her er hvad jeg nåede frem til:
SELECT a.unikey, a.dato as datostart, a.tid as tidstart, b.dato as datoslut, b.tid as tidslut, TIMEDIFF( (concat(b.dato," ", b.tid) ),(concat(a.dato," ", a.tid) ) ) FROM stat_147 a INNER JOIN stat_147 b ON a.unikey = b.unikey WHERE a.rutine = 1 AND b.rutine = 2 AND a.unikey <> "" ORDER BY a.dato ASC, a.tid ASC, b.dato DESC, b.dato DESC  LIMIT 500;

Problemet er bare at hvis jeg kører det her, så kopieres der 230.000 records over i tmp - jeg har indekseret flg :

id, unikey, dato,tid,rutine

Er der en venlig (og dygtig) sjæl som kan hjælpe her ??

Hilsen Mark
Avatar billede eksperten1 Nybegynder
01. september 2008 - 15:10 #1
Hov hov - det er kun hvis jeg "ORDER BY a.dato ASC, a.tid ASC, b.dato DESC, b.dato DESC " SÅ bliver der kopieret over i tmp - ellers går det hurtigt, men forkert.
Avatar billede eksperten1 Nybegynder
01. september 2008 - 15:29 #2
Her er et resultat af nogle data

select id,unikey,dato,tid,rutine from stat_147 where unikey = 'CK114779751DK' ORDER BY dato asc, tid asc
id    unikey    dato    tid    rutine
4559    CK114779751DK    2008-08-01    20:48:00    1
4560    CK114779751DK    2008-08-01    20:53:00    1
4575    CK114779751DK    2008-08-01    21:51:00    1
6839    CK114779751DK    2008-08-01    22:02:00    1
6940    CK114779751DK    2008-08-02    00:00:00    1
11719    CK114779751DK    2008-08-04    08:25:00    1
11720    CK114779751DK    2008-08-04    08:25:00    1
11748    CK114779751DK    2008-08-04    12:06:00    2
11749    CK114779751DK    2008-08-04    12:56:00    2

Så her vil jeg jo gerne have flg. resultat :
CK114779751DK, 2008-08-01, 20:48:00, 2008-08-04, 12:56:00, (antal timer imellem)

Måske det hjælper på at forklare hvad jeg ønsker :-)
Avatar billede eksperten1 Nybegynder
01. september 2008 - 15:30 #3
Nå ja - jeg lavede et index som fjernede mit tmp problem - så nu er det "kun" selve queryet
Avatar billede kjulius Novice
01. september 2008 - 19:16 #4
Prøv noget a. la. dette:

SELECT unikey,  mintimestamp, maxtimestamp, TIMEDIFF( sluttimestamp,starttimestamp) AS regtime
FROM (
  SELECT unikey, MIN(CONCAT.dato,' ', tid)) AS starttimestamp, MAX(CONCAT(dato, ' ', tid)) AS sluttimestamp
  FROM stat_147
  GROUP BY unikey
) AS a

eller er det for simpelt?
Avatar billede kjulius Novice
01. september 2008 - 19:18 #5
Øhh, skulle have været:

SELECT unikey,  starttimestamp, sluttimestamp, TIMEDIFF( sluttimestamp,starttimestamp) AS regtime
FROM (
  SELECT unikey, MIN(CONCAT.dato,' ', tid)) AS starttimestamp, MAX(CONCAT(dato, ' ', tid)) AS sluttimestamp
  FROM stat_147
  GROUP BY unikey
) AS a
Avatar billede eksperten1 Nybegynder
02. september 2008 - 10:31 #6
kjulius - din bandit :-) Det virker jo :-)))))

Hvordan kan jeg evt. optimere på mine index's så det er hurtigst muligt, hvad bør jeg indexere ?? (der kommer omkring 10.000 linjer hver dag, så der kommer til at være mange data i databasen)

Tak for hjælpen - husk at smide et svar
Avatar billede eksperten1 Nybegynder
02. september 2008 - 10:48 #7
hmm - det er ikke altid at træerne vokser ind i himlen :-)

Det er væsenligt at der både findes en rutine = 1 SAMT rutine = 2, det betyder at pakken både er modtaget og afleveret - det bliver ikke kontrolleret i det query - kan du få den med ind i ??

//Mark
Avatar billede eksperten1 Nybegynder
02. september 2008 - 10:49 #8
Det var derfor at jeg i mit join havde "a.rutine = 1 AND b.rutine = 2" - jeg har aldrig arbejedet med subquery's - så jeg kan ikke gennemskue hvordan det hænger sammen.

:-)
Mark
Avatar billede kjulius Novice
02. september 2008 - 18:37 #9
Betyder det, at kun unikey, hvor der både findes en rutine 1 og en rutine 2 skal medtages?

I så fald kunne den måske ændres til:

SELECT unikey,  starttimestamp, sluttimestamp, TIMEDIFF( sluttimestamp,starttimestamp) AS regtime
FROM (
  SELECT unikey, MIN(CONCAT.dato,' ', tid)) AS starttimestamp, MAX(CONCAT(dato, ' ', tid)) AS sluttimestamp
  FROM stat_147
  GROUP BY unikey
  HAVING SUM(CASE WHERE rutine=1 THEN 1 ELSE 0 END) > 0 AND SUM(CASE WHERE rutine=2 THEN 1 ELSE 0 END) > 0
) AS a
Avatar billede kjulius Novice
02. september 2008 - 18:39 #10
Hastværk er lastværk, som man siger. CASE WHERE skal være CASE WHEN i stedet.
Avatar billede kjulius Novice
02. september 2008 - 18:45 #11
Mht. index, så bør der være et index på unikey feltet. Derudover kan jeg ikke umiddelbart se nogen grund til at indexere andre felter på baggrund af denne query.
Avatar billede eksperten1 Nybegynder
04. september 2008 - 08:56 #12
Tak for hjælpen...

Jeg fortsætter lige tråden igen med dit navn i overskriften
Avatar billede eksperten1 Nybegynder
12. september 2008 - 13:18 #13
kjulius > hvis du fortsat lytter med så har jeg brug for din hjælp til at rette lidt op på resultatet...

Her er mit query - jeg ændrede lidt på tabelstrukturen, primært for at få mere "korrekte" feltnavne :

SELECT stregkode, kundenr,termnr,bilnr,chauf, DAYNAME(starttimestamp) as startday, DAYNAME(sluttimestamp) as endday ,starttimestamp, sluttimestamp,
IF (
((DATEDIFF( sluttimestamp,(IF(DAYOFWEEK(starttimestamp) = 6, DATE_ADD(starttimestamp, INTERVAL 2 day), starttimestamp))) <= 2) )
,  TRUE , FALSE ) as regok
  FROM
  (SELECT stregkode,kundenr,termnr,bilnr,chauf, MIN(datotid) AS starttimestamp, MAX(datotid) AS sluttimestamp FROM statdata
  GROUP BY stregkode HAVING SUM(CASE WHEN leveret=0 THEN 1 ELSE 0 END) > 0 AND SUM(CASE WHEN leveret=1 THEN 1 ELSE 0 END) > 0) AS a
  WHERE stregkode <> '' AND termnr = '010'
  ORDER BY termnr, bilnr, chauf

I ovenstående får jeg resultatet for en given bruger's scanning statistik - MEN problemet er (vist) at jeg kun får vist resultater hvor at det er den samme scanner som har udført BEGGE skanninger - (alle scanningerne)

Jeg vil gerne have et resultat hvor at mit WHERE termnr = '010' giver et resultat hvor at termnr 010 har lavet det SIDSTE scan (sluttimestamp) - enhver kan/må have lavet de tidligere scanninger.

Kan det lade sig gøre ??

Håber du kan/vil hjælpe med denne :-)

God weekend
//Mark
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