Объединение двух таблиц и получение данных из T1, которые не совпадают в T2 - PullRequest
2 голосов
/ 17 апреля 2020

Сначала позвольте мне поблагодарить вас за вашу помощь, и это, вероятно, действительно просто, и я просто дурачок

У меня есть таблица 'users' с fname, lname, usersnumber, active, shift I у меня есть вторая таблица 'checkin' с userID, dateandtime.

Я пытаюсь получить список всех, кто не зарегистрировался за последние 8 часов. Я сделал это, но только если они зарегистрировались хотя бы один раз в прошлом. Если они новый пользователь, я не могу понять, как заставить их отображаться.

Запрос, который у меня есть:

SELECT u.lname, u.fname, u.usersnumber FROM users u
LEFT JOIN checkin c
on u.usersnumber = c.usersnumber
WHERE u.active = 'Yes' and u.shift = 'Day' 
AND  c.dateandtime < DATE_ADD(NOW(), INTERVAL -8 HOUR)
ORDER By lname;

Ответы [ 5 ]

1 голос
/ 17 апреля 2020

Я бы просто использовал not exists для этого:

select u.*
from users u
where 
    u.active = 'Yes'
    and u.shift = 'Day'
    and not exists (
        select 1
        from checkin c 
        where 
            c.usersnumber = u.usersnumber
            and u.dateandtime >= now() - interval 8 hour
    )
order by u.lname

Коррелированный подзапрос проверяет, есть ли у данного пользователя какая-либо строка в checkin в течение последних 8 часов, а not exists гарантирует, что is none.

Это решение обычно равно или более эффективно, чем join, особенно с правильным индексом на месте - здесь это будет checkin(usersnumber, dateandtime).

0 голосов
/ 17 апреля 2020

Исправлено, если кто-то не видит что-то не так с ним

SELECT u.lname, u.fname, u.usersnumber FROM users u
LEFT JOIN checkin c
on u.usersnumber = c.usersnumber
WHERE u.active = 'Yes' and u.shift = 'Day' 
AND  (c.dateandtime < DATE_ADD(NOW(), INTERVAL -8 HOUR)
OR c.dateandtiime IS NULL)
ORDER By lname;

кредит моему коллеге Джо

0 голосов
/ 17 апреля 2020

Условие c.dateandtime < DATE_ADD(NOW(), INTERVAL -8 HOUR) получит пользователей, чье время последней регистрации было более 8 часов go, но для тех, у кого никогда не зарегистрировано, c.dateandtime будет NULL , поэтому вы должны учитывать и это условие.

Кроме того, вы должны принять во внимание, что пользователь, зарегистрировавший более 8 часов go, мог также проверить менее чем за 8 часов a go, так что вы действительно хотите только самую последнюю дату регистрации, поэтому я использую MAX().

SELECT u.lname, u.fname, u.usersnumber FROM users u
LEFT JOIN checkin c
on u.usersnumber = c.usersnumber
WHERE
    u.active = 'Yes' and u.shift = 'Day' 
    AND
    (
      MAX(c.dateandtime) < DATE_ADD(NOW(), INTERVAL -8 HOUR) OR c.dateandtime IS NULL             
    )
ORDER By lname;
0 голосов
/ 17 апреля 2020

Звучит так, будто вы ищете шаблон 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

РЕДАКТИРОВАТЬ: изменено <на> = для возврата пользователей, у которых нет регистрация за последние восемь часов

0 голосов
/ 17 апреля 2020

Ниже должен работать запрос:

SELECT u.lname, u.fname, u.usersnumber FROM users u
LEFT JOIN checkin c
on u.usersnumber = c.usersnumber 
AND  c.dateandtime between INTERVAL '-8' hh +SYSTIMESTAMP and SYSTIMESTAMP
WHERE u.active = 'Yes' and u.shift = 'Day' and  c.usersnumber is null
ORDER By lname;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...