Попробуйте:
;
WITH Ins as (
Select *
FROM HR_DTR_Device
WHERE InOutMode = 0
),
Outs as (
Select *
FROM HR_DTR_Device
WHERE InOutMode = 1
)
SELECT Ins.ID,
Ins.Employee_ID,
Ins.Date as [In],
(
SELECT Min(Outs.Date)
FROM Outs
WHERE Ins.Employee_ID = Outs.Employee_ID
AND Outs.Date > Ins.Date
) as [Out]
FROM Ins
WHERE Ins.Employee_ID = '104'
Что это делает:
- Разделяет
Ins
и Outs
, как если бы они были отдельными источниками данных. Использование Общих табличных выражений позволяет вам заранее задавать подзапросы и давать им имена. - Для каждой записи в
Ins
ищет наименьшую дату из Outs
это все еще больше, чем In
дата. (Это предполагает, что ваши записи завершены, и что у вас никогда не будет двух Ins
подряд, потому что кто-то забыл отключить время.) - Не делает никаких предположений о том, когда
Out
происходит дата, просто она позже In
даты (по определению). Таким образом, вам не нужно беспокоиться о том, ушел ли сотрудник позже в тот же день или в начале следующего дня (если у вас есть сотрудники, работающие в разные смены). - Также будут отображаться любые записи, в которых сотрудник работално еще не вышло.
Я думаю, что ваша большая ошибка была здесь:
(SELECT MAX(Date) FROM HR_DTR_Device XX
WHERE InOutMode = 1
AND XX.Employee_ID = AA.Employee_ID
AND CAST(XX.Date AS DATE) = CAST(AA.Date AS DATE)) AS 'Out'
Вы возвращаете самую большую дату для этого сотрудника, которая находится в ту же календарную дату (и является Out
). Но если сотрудник работает до следующего утра, дата изменится!
Вы можете исправить это, изменив свой тест на следующее:
CAST(DATEADD(d, -1, XX.Date) AS DATE) = CAST(AA.Date AS DATE)
... но тогда это произойдетРаботайте ТОЛЬКО для сотрудников, которые работали всю ночь, в то время как мое решение просто находит следующий раз, когда сотрудник отключился после того, как он включился, независимо от того, будет ли это тот же день, следующий день или следующая неделя!
Если вам нравится это решение, отметьте его как принятое. Спасибо.