Звучит так, будто вы ищете шаблон anti-join .
Мы начинаем с внешнего соединения. Вернуть все что угодно (в вашем случае, пользователей) вместе с сопоставлением чего угодно (в вашем случае, регистрация). В случае строки пользователя, у которой нет соответствующей строки регистрации, внешнее объединение возвращает строку пользователя без строки входа в систему. На самом деле, то, что делает MySQL (или один из способов думать о том, что он делает), - это изобретает подходящую фиктивную строку checkin
, которая состоит из всех значений NULL и служит заполнителем для соответствующей строки.
Если мы сможем обернуть наш мозг вокруг этого, мы готовы к хитрости.
Мы включаем в предложение WHERE
условие, которое исключает все строки, у которых была соответствующая строка из регистрироваться. Оставляя только строки, в которых есть фиктивная выдуманная совпадающая строка.
Мы можем отличить guish те, проверяя столбец из таблицы регистрации, который, как мы знаем, не будет NULL для какой-либо действительной совпадающей строки, но будет быть NULL для строк, которые не имеют соответствия.
Ваш запрос , поэтому close.
Одной небольшой проблемой является условие для c.dateandtime
, которое требует, чтобы значение столбца отличалось от NULL. Это делает внешнее соединение эквивалентным внутреннему соединению. (Любая фиктивная строка регистрации будет иметь значение NULL для столбца и не сможет удовлетворить это условие в предложении WHERE.)
Поэтому переместило это условие в предложение ON
в external join.
И чтобы превратить запрос в анти-объединение, мы можем добавить условие, для которого usersnumber
из строки регистрации должно быть NULL. (Любая допустимая совпадающая строка будет иметь значение, отличное от NULL, мы гарантируем, что столбец должен быть не NULL, чтобы удовлетворять условию =
в предложении ON.)
Примерно так:
SELECT u.lname
, u.fname
, u.usersnumber
FROM users u
-- anti-join exclude rows that have a matching row in checkin
LEFT
JOIN checkin c
ON c.usersnumber = u.usersnumber
AND c.dateandtime >= NOW() + INTERVAL -8 HOUR
WHERE c.usersnumber IS NULL
--
AND u.active = 'Yes'
AND u.shift = 'Day'
ORDER
BY u.lname
Существуют и другие шаблоны запросов, которые дают эквивалентный результат.
Некоторым легче обернуть мозг вокруг НЕ СУЩЕСТВУЮЩЕГО с помощью коррелированного подзапроса. Иногда это не так эффективно, как шаблон предотвращения объединения (подходящие доступные индексы обязательны для производительности на больших наборах)
SELECT u.lname
, u.fname
, u.usersnumber
FROM users u
WHERE NOT EXISTS
( SELECT 1
FROM checkin c
WHERE c.usersnumber = u.usersnumber
AND c.dateandtime >= NOW() + INTERVAL -8 HOUR
)
AND u.active = 'Yes'
AND u.shift = 'Day'
ORDER
BY u.lname
РЕДАКТИРОВАТЬ: изменено <на> = для возврата пользователей, у которых нет регистрация за последние восемь часов