Jeśli „nieobecność” jest zdefiniowana jako brak wiersza w emp_tx
tabela dla konkretnego empcode
dla określonej daty (data=północ do północy 24-godzinny okres) i ...
Jeśli dopuszczalne jest nie pokazywanie "nieobecności" dla daty, gdy nie ma żadnych transakcji w emp_tx
tabela dla tej daty (tj. wyklucz datę, w której WSZYSTKIE empcode są nieobecne w tym dniu), a następnie ...
Możesz uzyskać pierwsze cztery kolumny określonego zestawu wyników za pomocą zapytania takiego:(nieprzetestowane)
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
Pobieranie piątej kolumny TotalNoofAbsent
zwrócone w tym samym zestawie wyników jest możliwe, ale spowoduje to, że zapytanie będzie naprawdę nieuporządkowane. Ten szczegół może być efektywniej obsługiwany po stronie klienta podczas przetwarzania zwróconego zestawu wyników.
Jak działa zapytanie
Widok wbudowany aliasem d
otrzymuje zestaw wartości "dat", które sprawdzamy. Korzystanie z emp_tx
tabela jako źródło tych wartości „daty” jest wygodnym sposobem na zrobienie tego. Nie DATE()
funkcja zwraca tylko część "daty" argumentu DATETIME; używamy GROUP BY
aby uzyskać odrębną listę dat (tj. bez zduplikowanych wartości). (W przypadku tego zapytania wbudowanego widoku szukamy odrębnego zestawu wartości DATE między dwiema wartościami przekazanymi jako argumenty. Istnieją inne, bardziej skomplikowane sposoby generowania listy wartości DATE).
Tak długo, jak każda wartość „data”, którą uznasz za „nieobecność”, pojawi się gdzieś w tabeli (to znaczy, co najmniej jeden empcode
miał jedną transakcję w każdym interesującym dniu) i tak długo, jak liczba wierszy w emp_tx
tabela nie jest nadmierna, wtedy zapytanie wbudowanego widoku będzie działać dość dobrze.
(UWAGA:zapytanie w widoku wbudowanym można uruchomić osobno, aby sprawdzić, czy wyniki są poprawne i zgodne z oczekiwaniami.)
Następnym krokiem jest wykonanie wyników z widoku wbudowanego i wykonanie CROSS JOIN
operacja (w celu wygenerowania produktu kartezjańskiego) pasująca do KAŻDEGO empcode
z KAŻDĄ date
zwrócone z widoku wbudowanego. Wynik tej operacji reprezentuje każde możliwe wystąpienie „obecności”.
Ostatnim krokiem w zapytaniu jest wykonanie operacji „anti-join” przy użyciu LEFT JOIN
i WHERE IS NULL
orzec. LEFT JOIN
(złączenie zewnętrzne) zwraca każde możliwe wystąpienie obecności (od lewej strony), WŁĄCZNIE z tymi, które nie mają pasującego wiersza (rekordu obecności) z emp_tx
tabela.
„Sztuczka” polega na dołączeniu predykatu (w klauzuli WHERE), który odrzuca wszystkie wiersze, w których znaleziono pasujący rekord obecności, tak aby pozostały tylko wszystkie kombinacje empcode
i date
(możliwe wystąpienia obecności), gdy nie było transakcji obecności NIE DOPASOWANE.
(UWAGA:Celowo pozostawiłem odniesienia do kolumny s_date (DATETIME) jako „puste” w predykatach i użyłem predykatów zakresu. Pozwoli to MySQL na efektywne wykorzystanie odpowiedniego indeksu, który zawiera tę kolumnę.)
Gdybyśmy mieli zawinąć odwołania do kolumn w predykaty wewnątrz funkcji, np. DATE(p.s_date)
, wtedy MySQL nie będzie w stanie efektywnie wykorzystać indeksu w s_date
kolumna.
Jak wskazuje jeden z komentarzy (do Twojego pytania), nie rozróżniamy transakcji, które oznaczają pracownika jako „przychodzące” lub „wychodzące”. Szukamy TYLKO istnienia transakcji dla tego kodu empcode w określonym 24-godzinnym okresie „od północy do północy”.
Istnieją inne podejścia do uzyskania tego samego zestawu wyników, ale wzorzec „anty-łączenia” zwykle okazuje się zapewniać najlepszą wydajność przy dużych zestawach.
Aby uzyskać najlepszą wydajność, prawdopodobnie będziesz potrzebować pokrycia indeksów:
... ON master (empcode, name, dept)
... ON emp_tx (s_date, empcode)