Допустим, у меня есть таблица BQ с именем employee_entries
, которая создается и заполняется следующими операторами SQL:
CREATE OR REPLACE TABLE `my-project.my_dataset.employee_entries` (
employee_id STRING,
entry_datetime DATETIME,
entry_flag STRING,
salary FLOAT64
);
INSERT INTO `my-project.my_dataset.employee_entries`
(employee_id, entry_datetime, entry_flag, salary) VALUES
('1234', '2023-07-15 08:00:00', '1', 50000),
('1234', '2023-07-15 09:00:00', '2', 50000),
('1234', '2023-07-15 10:00:00', '0', 50000),
('1234', '2023-07-15 11:00:00', '0', 50000),
('1234', '2023-07-15 12:00:00', '1', 50000),
('1234', '2023-07-15 13:00:00', '3', 50000),
('5678', '2023-07-15 08:30:00', '2', 75000),
('5678', '2023-07-15 09:30:00', '2', 75000),
('5678', '2023-07-15 10:30:00', '3', 75000),
('5678', '2023-07-15 11:30:00', '4', 75000),
('5678', '2023-07-15 12:30:00', '4', 75000),
('5678', '2023-07-15 13:30:00', '0', 75000);
Это создаст следующий пример таблицы:
Проблема:
Я хочу создать новую таблицу с именем employees_extended
, в которой есть все столбцы этой таблицы, кроме одного дополнительного столбца, называемого вход_переключатели.
Этот столбец enter_switches рассчитывается на основе других столбцов как таковых:
Для каждой строки исходной таблицы employee_entries
для конкретного сотрудника_id, если все строки для этого сотрудника расположены в хронологическом порядке в соответствии с записью_дата-время,
Я хочу подсчитать, сколько раз значение входного флага переключалось до текущей строки за последние 2 часа;
«переключатель», означающий, что значение различается для двух хронологически последовательных записей для этого идентификатора сотрудника.
Например, если в хронологическом порядке за последние 2 часа было 4 записи для сотрудника_id='1234' до текущей строки, и 4 значения входного_флага были ['1',NULL,'0','0'] тогда это возвращает значение enter_switches, равное 2 для этой строки («переключение» с 1 на NULL, а затем с NULL на 0. Переход от 0 к 0 не включается в качестве переключателя).
Таблица ожидаемых результатов:
Как я могу получить эту результирующую таблицу, используя SQL-запрос в BQ?
Редактировать:
Моя попытка:
WITH prev_entries AS ( -- get previous entry flags for each row
SELECT *,
LAG(entry_flag) OVER (PARTITION BY employee_id ORDER BY entry_datetime) AS prev_entry_flag,
ROW_NUMBER() OVER (PARTITION BY employee_id ORDER BY entry_datetime) AS rn
FROM `my-project.my_dataset.employee_entries`
),
-- deals with situation where there is no previous entry for an employee_id
entry_changes AS (
SELECT * EXCEPT (prev_entry_flag, rn),
CASE
WHEN rn=1 THEN entry_flag
ELSE prev_entry_flag
END AS prev_entry_flag
FROM prev_entries
)
SELECT employee_id,
entry_datetime,
entry_flag,
prev_entry_flag,
-- trying find a way to change this window fucntion to only count the last 2 hours, or find an alternative solution to this problem
SUM(CASE WHEN entry_flag IS DISTINCT FROM prev_entry_flag THEN 1 ELSE 0 END) OVER (PARTITION BY employee_id ORDER BY entry_datetime ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS switch_count
FROM entry_changes
ORDER BY
employee_id, entry_datetime;
Пример решения этой проблемы с помощью MySQL: dbfiddle.uk/9n-4U6s4 (я не знаком с google-bigquery, но, может быть, вы можете это переписать?)
Что такое количество входных_переключателей для последовательности (3,4,0,4)?
@ValNik Счетчик для этой последовательности будет 3. Один для переключения с 3 на 4, один для переключения с 4 на 0 и один для 0 на 4. Это предполагает, что это входные флаги для одного и того же сотрудника_id за последние 2 часа.
@Luuk На самом деле я не считаю отдельные флаги, я считаю, сколько раз они менялись. Таким образом, один и тот же флаг может повторяться. например если флаги за последние 2 часа для определенного идентификатора сотрудника равны [1,2,1,2], то значение входного_переключателя для этой строки будет равно 3.
В примере ['1',NULL,'0','0']
что такое NULL?
@thegreenchipmunk: Должно быть, я неправильно прочитал/неправильно понял эту часть вопроса. Где ваша попытка решения этой проблемы?
@thegreenchipmunk: вы можете изменить его так: dbfiddle.uk/shBpDI5C
@GeorrinaSkibinski и Луук, я добавил свою попытку решения.
@ValNik NULL — это просто значение NULL для входного_флага. Этот столбец также может содержать значения NULL. NULL рассматривается как любое другое значение. т. е. изменение любого другого значения на NULL считается «переключением». NULL на NULL не является переключением.
@Luuk Мое решение на данный момент похожее. Проблема возникает, когда я пытаюсь ограничить окно подсчета переключений только двумя последними часами. Все, что было сделано до последних 2 часов, игнорируется. Я добавил свою попытку решения, которая делает что-то похожее на то, что у вас есть.
Если мы не можем использовать диапазон строк по времени в оконной функции, мы можем (самостоятельно) ПРИСОЕДИНИТЬСЯ к таблице с условием как
ROWS between <preceding 2 hours> and current
Мы используем сравнение как >=
и <=
, чтобы включить первую и текущую строку.
Затем вычислите изменения входного флага для каждой строки левой таблицы.
См. пример
with ranged_data as(
select ee1.*
,ee2.id id2,ee2.entry_datetime edt2, ee2.entry_flag flag2
,case when lag(ee2.entry_flag,1,ee2.entry_flag)
over(partition by ee1.employee_id,ee1.entry_datetime
order by ee2.entry_datetime)
=ee2.entry_flag
then 0
when coalesce(
lag(ee2.entry_flag,1,ee2.entry_flag)
over(partition by ee1.employee_id,ee1.entry_datetime
order by ee2.entry_datetime)
,ee2.entry_flag) is null
then 0
else 1
end chng_flag
from employee_entries ee1
left join employee_entries ee2 on ee1.employee_id=ee2.employee_id
and ee2.entry_datetime<=ee1.entry_datetime
-- and ee2.entry_datetime>=dateadd(hour,-2,ee1.entry_datetime)
and ee2.entry_datetime>=datetime_add(ee1.entry_datetime, interval -2 hour)
)
select id,employee_id,entry_datetime,entry_flag,salary
,sum(chng_flag) entry_switches
,string_agg(id2,'-') ids
,string_agg(case when flag2 is null then 'null' else flag2 end,'-') entry_flags
from ranged_data
group by employee_id,entry_datetime,entry_flag,salary
Использовалась упрощенная проверка для null=null
, поскольку значение Coalesce(null, null) равно нулю. Может быть, здесь лучше ОТЛИЧНО ОТ.
Я добавлю столбец id в таблицу только для ясности.
Версия, отредактированная для BQ, мне помогла, спасибо. Я просто добавлю, что для BQ (как и для некоторых других баз данных) значение по умолчанию, передаваемое в функцию LAG, должно быть константой, поэтому значения NULL не могут обрабатываться таким образом. Но это решило мою проблему в целом.
поделитесь, пожалуйста, попыткой решения этой проблемы.