Есть ли способ изменить это самообъединение BigQuery для использования оконной функции? - PullRequest
0 голосов
/ 09 июля 2019

Допустим, у меня есть таблица «событий» таблицы BigQuery (на самом деле это медленный подзапрос), в которой хранится количество событий в день по типам событий. Существует много типов событий, и большинство из них не происходит в большинстве дней, поэтому есть только строка для комбинаций типа день / событие с ненулевым счетом.

У меня есть запрос, который возвращает счетчик для каждого типа события и дня и счетчик для этого события из N дней назад, который выглядит следующим образом:

WITH events AS (
  SELECT DATE('2019-06-08') AS day, 'a' AS type, 1 AS count
  UNION ALL SELECT '2019-06-09', 'a', 2
  UNION ALL SELECT '2019-06-10', 'a', 3
  UNION ALL SELECT '2019-06-07', 'b', 4
  UNION ALL SELECT '2019-06-09', 'b', 5
)
SELECT e1.type, e1.day, e1.count, COALESCE(e2.count, 0) AS prev_count
FROM events e1
LEFT JOIN events e2 ON e1.type = e2.type AND e1.day = DATE_ADD(e2.day, INTERVAL 2 DAY) -- LEFT JOIN, because the event may not have occurred at all 2 days ago
ORDER BY 1, 2

Запрос медленный. Рекомендации BigQuery рекомендуют использовать оконные функции вместо самостоятельных объединений. Есть ли способ сделать это здесь? Я мог бы использовать функцию LAG, если бы для каждого дня была строка, но ее нет. Могу ли я как-нибудь "дополнить"? (Нет краткого списка возможных типов событий. Конечно, я мог бы присоединиться к SELECT DISTINCT type FROM events, но это, вероятно, не будет быстрее, чем само-присоединение.)

Ответы [ 2 ]

2 голосов
/ 09 июля 2019

Метод грубой силы:

select e.*,
       (case when lag(day) over (partition by type order by date) = dateadd(e.day, interval -2 day)
             then lag(cnt) over (partition by type order by date)
             when lag(day, 2) over (partition by type order by date) = dateadd(e.day, interval -2 day)
             then lag(cnt, 2) over (partition by type order by date)
        end) as prev_day2_count
from events e;

Это прекрасно работает при двухдневном лаге.Это становится более громоздким для более длинных лагов.

EDIT:

В более общей форме используются оконные рамы.К сожалению, они должны быть числовыми, так что есть дополнительный шаг:

select e.*,
       (case when min(day) over (partition by type order by diff range between 2 preceding and current day) = date_add(day, interval -2 day)
             then first_value(cnt) over (partition by type order by diff range between 2 preceding and current day)
        end)
from (select e.*,
             date_diff(day, max(day) over (partition by type), day) as diff   -- day is a bad name for a column because it is a date part
      from events e
     ) e;

И дух!Выражение case необязательно:

select e.*,
       first_value(cnt) over (partition by type order by diff range between 2 preceding and 2 preceding)
from (select e.*,
             date_diff(day, max(day) over (partition by type), day) as diff   -- day is a bad name for a column because it is a date part
      from events e
     ) e;
1 голос
/ 11 июля 2019

Ниже для BigQuery Standard SQL

#standardSQL
SELECT *, IFNULL(FIRST_VALUE(count) OVER (win), 0) prev_count
FROM `project.dataset.events`
WINDOW win AS (PARTITION BY type ORDER BY UNIX_DATE(day) RANGE BETWEEN 2 PRECEDING AND 2 PRECEDING)   

Если t применить к образцу данных из вашего вопроса - результат:

Row day         type    count   prev_count   
1   2019-06-08  a       1       0    
2   2019-06-09  a       2       0    
3   2019-06-10  a       3       1    
4   2019-06-07  b       4       0    
5   2019-06-09  b       5       4    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...