Hvis et "fravær" er defineret som manglende fremkomst af en række i emp_tx
tabel for en bestemt empcode
for en bestemt dato (dato=midnat til midnat 24 timers periode), og ...
Hvis det er acceptabelt ikke at vise et "fravær" for en dato, hvor der INGEN transaktioner er i emp_tx
tabel for den dato (dvs. ekskluder en dato, hvor ALLE empkoder er fraværende på den dato), så ...
Du kan få de første fire kolonner i det angivne resultatsæt med en forespørgsel som denne:(utestet)
SELECT m.empcode AS `EmpCode`
, m.name AS `EmpName`
, m.dept AS `Department`
, d.dt AS `AbsentDate`
FROM ( SELECT DATE(t.s_date) AS dt
FROM emp_tx t
WHERE t.s_date >= '2012-12-12'
AND t.s_date < DATE_ADD( '2012-12-20' ,INTERVAL 1 DAY)
GROUP BY DATE(t.s_date)
ORDER BY DATE(t.s_date)
) d
CROSS
JOIN master m
LEFT
JOIN emp_tx p
ON p.s_date >= d.dt
AND p.s_date < d.dt + INTERVAL 1 DAY
AND p.empcode = m.empcode
WHERE p.empcode IS NULL
ORDER
BY m.empcode
, d.dt
Får den femte kolonne TotalNoofAbsent
returneret i det samme resultatsæt er muligt, men det vil gøre den forespørgsel virkelig rodet. Denne detalje kan muligvis håndteres mere effektivt på klientsiden, når det returnerede resultatsæt behandles.
Sådan fungerer forespørgslen
Den indbyggede visning kaldet d
giver os et sæt "dato"-værdier, som vi tjekker. Brug af emp_tx
tabel som en kilde til disse "dato"-værdier er en bekvem måde at gøre dette på. Ikke DATE()
funktionen returnerer kun "dato"-delen af DATETIME-argumentet; vi bruger en GROUP BY
for at få en særskilt liste over datoer (dvs. ingen duplikerede værdier). (Det, vi leder efter, med denne inline-visningsforespørgsel, er et særskilt sæt DATE-værdier mellem de to værdier, der sendes ind som argumenter. Der er andre, mere involverede, måder at generere en liste over DATE-værdier på.)
Så længe hver "dato"-værdi, som du vil betragte som et "fravær", vises et sted i tabellen (det vil sige mindst én empcode
havde en transaktion på hver dato, der er af interesse), og så længe antallet af rækker i emp_tx
tabellen ikke er overdreven, så vil den inline-visningsforespørgsel fungere rimeligt godt.
(BEMÆRK:Forespørgslen i den indbyggede visning kan køres separat for at bekræfte, at resultaterne er korrekte og som vi forventer.)
Det næste trin er at tage resultaterne fra inline-visningen og udføre en CROSS JOIN
operation (for at generere et kartesisk produkt) for at matche HVER empcode
med HVER date
vendt tilbage fra inline-visningen. Resultatet af denne operation repræsenterer enhver mulig forekomst af "deltagelse".
Det sidste trin i forespørgslen er at udføre en "anti-join"-operation ved hjælp af en LEFT JOIN
og en WHERE IS NULL
prædikat. LEFT JOIN
(ydre joinforbindelse) returnerer alle mulige tilstedeværelsesforekomster (fra venstre side), HERUNDER dem, der ikke har en matchende række (deltagelsesrekord) fra emp_tx
tabel.
"Tricket" er at inkludere et prædikat (i WHERE-sætningen), der kasserer alle de rækker, hvor der blev fundet en matchende tilstedeværelsesrecord, så det, vi står tilbage med, er alle kombinationer af empcode
og date
(mulige tilstedeværelsesforekomster), hvor der IKKE var INGEN MATCHENDE fremmødetransaktion.
(BEMÆRK:Jeg har målrettet efterladt referencerne til s_date (DATETIME) kolonnen "bare" i prædikaterne, og brugt interval prædikater. Dette vil gøre det muligt for MySQL at gøre effektiv brug af et passende indeks, der inkluderer den kolonne.)
Hvis vi skulle pakke kolonnehenvisningerne ind i prædikaterne inde i en funktion, f.eks. DATE(p.s_date)
, så vil MySQL ikke være i stand til at gøre effektiv brug af et indeks på s_date
kolonne.
Som en af kommentarerne (på dit spørgsmål) påpeger, skelner vi ikke mellem transaktioner, der markerer en medarbejder, enten som "kommer ind" eller "går ud". Vi leder KUN efter eksistensen af en transaktion for den empcode i en given 24-timers "midnat til midnat" periode.
Der er andre tilgange til at få det samme resultatsæt, men "anti-sammenføjningsmønsteret" viser sig normalt at give den bedste ydeevne med store sæt.
For den bedste ydeevne vil du sandsynligvis have dækkende indekser:
... ON master (empcode, name, dept)
... ON emp_tx (s_date, empcode)