#sql #sql-server
#sql #sql-сервер
Вопрос:
В настоящее время я работаю с системой синхронизации, в которой время включения и выключения определяется в отдельных строках, как показано ниже:
|ID | Clocking System | Employee | Date | Time | Clocking In / Out|
------------------------------------------------------------------------
|1 | System A | John Doe | 1 Dec 20 | 05:30 | In |
|2 | System B | John Doe | 1 Dec 20 | 17:45 | Out |
Я хотел бы изменить это представление, в котором значения синхронизации и синхронизации отображаются как единое целое, например:
|ID | Clocking System | Employee | Date | Clocking In | Clocked Out |
---------------------------------------------------------------------------------
|1 | System A | John Doe | 1 Dec 20 | 05:30 | 17:45 |
Я был бы очень признателен за любую помощь.
Заранее благодарю вас.
Комментарии:
1. Знаете ли вы, что записи ввода / вывода всегда чередуются? Если нет, объясните, что делать с аномалиями.
2. Изучите, как сводить данные в SQL Server. Вы можете выполнить сводку, используя
PIVOT
, например, конструкцию T-SQL.3. @GordonLinoff Извиняюсь, я довольно новичок в SQL. Чередование? Как выполняется отдельно? Существует случай, когда поле «Out» может быть нулевым. У меня есть хранимая процедура для обслуживания изменяющихся записей.
4. Вы должны предоставить способ идентификации сотрудника, отличный от просто имени, например. Employee_ID . Я бы сделал два отдельных запроса для времени входа и выхода, а затем объединил их.
Ответ №1:
У меня была очень похожая проблема при генерации сдвигов из данных событий хоккейных матчей. Время начала и окончания сдвигов было смешано с другими событиями. Было несколько игроков и несколько игр. Таблица eventTable
была похожа на эту:
| player | game | event | time_sec |
-----------------------------------------------
| A | game-1 | shift-start | 10 |
| B | game-1 | shift-start | 11 |
| A | game-1 | shot | 12 |
| A | game-1 | shift-end | 50 |
| B | game-1 | pass | 55 |
| B | game-1 | shift-end | 60 |
| A | game-1 | shift-start | 90 |
| A | game-1 | shift-end | 120 |
... ... ... ...
| X | game-n | ... | ... |
Я хотел изменить форму данных, чтобы было легко отвечать на такие вопросы, как:
- Сколько смен было у игрока 1 в игре 1?
- Какой сдвиг был самым длинным (по игроку, по игре)?
- Какова была средняя продолжительность смен у игрока 1?
Затем желаемый результат shiftTable
был:
| shift_number | player | game | shift_start | shift_end | duration |
-----------------------------------------------------------------------
| 1 | A | game-1 | 10 | 50 | 40 |
| 2 | A | game-1 | 90 | 120 | 30 |
| 1 | B | game-1 | 11 | 60 | 49 |
... ... ... ... ... ...
| n | X | game-n | ... | ... | ... |
Мое решение состояло в том, чтобы создать подзапрос для каждого shift_start
и shift_end
с новым значением shift_number
, вызываемым функцией row_number()
SQLITE, которое увеличивается при новом экземпляре события смены для каждого игрока и сбрасывается при каждой новой игре, показано ниже для shift_start
:
SELECT
row_number() OVER (
PARTITION BY player, game
ORDER BY time_sec) shift_number,
player,
game_name,
time_sec AS shift_start
FROM
eventTable
WHERE
event = 'shift_start'
Который произвел:
(subquery shift_start)
| shift_number | player | game | shift_start |
------------------------------------------------
| 1 | A | game-1 | 10 |
| 2 | A | game-1 | 90 |
| 1 | B | game-1 | 11 |
(subquery shift_end)
| shift_number | player | game | shift_end |
------------------------------------------------
| 1 | A | game-1 | 50 |
| 2 | A | game-1 | 120 |
| 1 | B | game-1 | 60 |
При объединении player, game, shift_number
создается нужная выходная таблица shiftTable
.
Полный запрос для создания shiftTable
с использованием SQLITE выглядит следующим образом:
SELECT
shift_number,
player,
game,
shift_start,
shift_end,
shift_end - shift_start AS duration
FROM
(SELECT
row_number() OVER (
PARTITION BY player, game
ORDER BY time_sec) shift_number,
player,
game_name,
time_sec AS shift_start
FROM eventTable
WHERE event = 'shift_start')
LEFT JOIN
(SELECT
row_number() OVER (
PARTITION BY player, game
ORDER BY time_sec) shift_number,
player,
game_name,
time_sec AS shift_end
FROM eventTable
WHERE event = 'shift_end')
USING (player, game, shift_number)
Я использовал LEFT JOIN
вместо JOIN
, потому что было возможно, что в a shift_start
отсутствует его пара shift_end
, и наоборот. Это вводит a NULL
для отсутствующей пары, которую я мог бы затем поймать и обработать.
В shiftTable
я чувствовал, что было бы лучше предварительно рассчитать продолжительность для моего рабочего процесса, и с объемами данных, с которыми я имел дело, это означало, что компромисс между пространством и временем не повлиял на меня в любом случае.
Ответ №2:
Предполагая, что ваши данные не имеют аномалий, это легко обрабатывается с помощью lead()
select t.*
from (select t.*,
lead(time) over (partition by employee, date order by time) as checkout_time
from t
) t
where in_out = 'In';
Предполагается, что не существует случаев, когда отсутствуют записи ввода / вывода или записи одного и того же времени подряд.