Ако "отсъствието" е дефинирано като непоказване на ред в emp_tx
таблица за конкретен empcode
за определена дата (дата=24-часов период от полунощ до полунощ) и ...
Ако е приемливо да не се показва „отсъствие“ за дата, когато НЯМА транзакции в emp_tx
таблица за тази дата (т.е. изключете дата, когато ВСИЧКИ empcode отсъстват на тази дата), тогава ...
Можете да получите първите четири колони от посочения набор от резултати със заявка като тази:(нетествано)
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
Получаване на тази пета колона TotalNoofAbsent
върнати в същия набор от резултати е възможно, но това ще направи тази заявка наистина объркана. Тази подробност може да бъде по-ефективно обработена от страна на клиента при обработката на върнатия набор от резултати.
Как работи заявката
Вграденият изглед с псевдоним като d
ни получава набор от стойности на "дата", които проверяваме. Използване на emp_tx
таблицата като източник на тези стойности на "дата" е удобен начин да направите това. Не DATE()
функцията връща само частта "дата" от аргумента DATETIME; ние използваме GROUP BY
за да получите отделен списък с дати (т.е. без дублиращи се стойности). (Това, което преследваме, с тази заявка за вграден изглед, е отделен набор от стойности на DATE между двете стойности, предадени като аргументи. Има други, по-ангажирани начини за генериране на списък от стойности на DATE.)
Докато всяка стойност на „дата“, която смятате за „отсъствие“, се появява някъде в таблицата (т.е. поне един empcode
имаше една транзакция на всяка дата, която представлява интерес), и докато броят редове в emp_tx
таблица не е прекомерна, тогава заявката за вграден изглед ще работи сравнително добре.
(ЗАБЕЛЕЖКА:Заявката във вградения изглед може да се изпълнява отделно, за да се провери дали резултатите са правилни и както очакваме.)
Следващата стъпка е да вземете резултатите от вградения изглед и да извършите CROSS JOIN
операция (за генериране на декартово произведение), за да съответства на ВСЕКИ empcode
с ВСЯКА date
върнати от вградения изглед. Резултатът от тази операция представлява всяка възможна поява на "присъствие".
Последната стъпка в заявката е да се извърши операция "анти-присъединяване", като се използва LEFT JOIN
и WHERE IS NULL
предикат. LEFT JOIN
(външно присъединяване) връща всяко възможно събитие за присъствие (от лявата страна), ВКЛЮЧИТЕЛНО тези, които нямат съответстващ ред (запис на присъствие) от emp_tx
таблица.
„Номерът“ е да включите предикат (в клаузата WHERE), който отхвърля всички редове, където е намерен съответстващ запис на присъствие, така че това, което ни остава, са всички комбинации от empcode
и date
(възможни случаи на присъствие), където НЯМА СЪОТВЕТСТВАЩА транзакция за присъствие.
(ЗАБЕЛЕЖКА:Нарочно оставих препратките към колоната s_date (DATETIME) „голи“ в предикатите и използвах предикати за диапазон. Това ще позволи на MySQL да използва ефективно подходящ индекс, който включва тази колона.)
Ако трябваше да обвием препратките към колони в предикатите вътре във функция, напр. DATE(p.s_date)
, тогава MySQL няма да може да използва ефективно индекс на s_date
колона.
Както се посочва в един от коментарите (по вашия въпрос), ние не правим никаква разлика между транзакциите, които обозначават служител като „влизащ“ или „излизащ“. Ние търсим САМО съществуването на транзакция за този empcode в даден 24-часов период от „полунощ до полунощ“.
Има и други подходи за получаване на същия набор от резултати, но моделът „против присъединяване“ обикновено се оказва, че дава най-добра производителност при големи набори.
За най-добра производителност вероятно ще искате да покривате индекси:
... ON master (empcode, name, dept)
... ON emp_tx (s_date, empcode)