Для таблицы BQ SQL подсчитайте, сколько раз значение в данном столбце переключается за заданный период времени, разделенное по значению идентификатора

Допустим, у меня есть таблица 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);

Это создаст следующий пример таблицы:

идентификатор сотрудника запись_датавремя запись_флаг зарплата 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 6 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 не включается в качестве переключателя).

Таблица ожидаемых результатов:

идентификатор сотрудника запись_датавремя запись_флаг зарплата запись_переключатели 1234 2023-07-15 08:00:00 1 50000 0 1234 2023-07-15 09:00:00 2 50000 1 1234 2023-07-15 10:00:00 0 50000 2 1234 2023-07-15 11:00:00 0 50000 1 1234 2023-07-15 12:00:00 1 50000 1 1234 2023-07-15 13:00:00 3 50000 2 5678 2023-07-15 08:30:00 2 75000 0 5678 2023-07-15 09:30:00 2 75000 0 5678 2023-07-15 10:30:00 3 75000 1 5678 2023-07-15 11:30:00 4 75000 2 5678 2023-07-15 12:30:00 4 75000 1 5678 2023-07-15 13:30:00 6 75000 1

Как я могу получить эту результирующую таблицу, используя 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;

поделитесь, пожалуйста, попыткой решения этой проблемы.

Georgina Skibinski 21.07.2024 10:27

Пример решения этой проблемы с помощью MySQL: dbfiddle.uk/9n-4U6s4 (я не знаком с google-bigquery, но, может быть, вы можете это переписать?)

Luuk 21.07.2024 10:35

Что такое количество входных_переключателей для последовательности (3,4,0,4)?

ValNik 21.07.2024 13:03

@ValNik Счетчик для этой последовательности будет 3. Один для переключения с 3 на 4, один для переключения с 4 на 0 и один для 0 на 4. Это предполагает, что это входные флаги для одного и того же сотрудника_id за последние 2 часа.

thegreenchipmunk 21.07.2024 18:45

@Luuk На самом деле я не считаю отдельные флаги, я считаю, сколько раз они менялись. Таким образом, один и тот же флаг может повторяться. например если флаги за последние 2 часа для определенного идентификатора сотрудника равны [1,2,1,2], то значение входного_переключателя для этой строки будет равно 3.

thegreenchipmunk 21.07.2024 18:56

В примере ['1',NULL,'0','0'] что такое NULL?

ValNik 21.07.2024 19:10

@thegreenchipmunk: Должно быть, я неправильно прочитал/неправильно понял эту часть вопроса. Где ваша попытка решения этой проблемы?

Luuk 21.07.2024 19:56

@thegreenchipmunk: вы можете изменить его так: dbfiddle.uk/shBpDI5C

Luuk 21.07.2024 20:04

@GeorrinaSkibinski и Луук, я добавил свою попытку решения.

thegreenchipmunk 21.07.2024 20:19

@ValNik NULL — это просто значение NULL для входного_флага. Этот столбец также может содержать значения NULL. NULL рассматривается как любое другое значение. т. е. изменение любого другого значения на NULL считается «переключением». NULL на NULL не является переключением.

thegreenchipmunk 21.07.2024 20:22

@Luuk Мое решение на данный момент похожее. Проблема возникает, когда я пытаюсь ограничить окно подсчета переключений только двумя последними часами. Все, что было сделано до последних 2 часов, игнорируется. Я добавил свою попытку решения, которая делает что-то похожее на то, что у вас есть.

thegreenchipmunk 21.07.2024 20:25
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
11
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если мы не можем использовать диапазон строк по времени в оконной функции, мы можем (самостоятельно) ПРИСОЕДИНИТЬСЯ к таблице с условием как

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 в таблицу только для ясности.

Демо для SQL Server

Версия, отредактированная для BQ, мне помогла, спасибо. Я просто добавлю, что для BQ (как и для некоторых других баз данных) значение по умолчанию, передаваемое в функцию LAG, должно быть константой, поэтому значения NULL не могут обрабатываться таким образом. Но это решило мою проблему в целом.

thegreenchipmunk 22.07.2024 08:25

Другие вопросы по теме