SQL присоединяется по разнице меток времени из той же таблицы - PullRequest
0 голосов
/ 08 мая 2018

Я не уверен, как написать этот SQL-запрос в BigQuery. У меня есть таблица событий с именами и временными метками. Допустим, у меня есть только два события в таблице: A и B. То, что я хочу сделать, - это запросить таблицу, чтобы получить все экземпляры события A, и получить следующее ближайшее вхождение B, и создать новый столбец с разницей во времени. B всегда будет происходить после A.

Например, если бы у меня была таблица, которая выглядит следующим образом:

A1 | 1:00 pm
B5 | 2:00 pm
A3 | 3:00 pm
B9 | 5:00 pm

Моя результирующая таблица будет:

A1 | 1 hour
A3 | 2 hours

Я пришел к следующему запросу:

SELECT
CAST(TIMESTAMP_DIFF((SELECT MIN(sub.time)
FROM table sub
WHERE sub.time > main.time), main.time, SECOND) AS INT64) duration 
FROM table main

Это прекрасно работает для получения таблицы, которую я хотел выше, но я также хотел бы добавить дополнительный столбец из подзапроса. Нечто похожее:

A1 | 1 hour  | B5Column
A3 | 2 hours | B9Column

Я попытался использовать следующий запрос:

SELECT
(SELECT
 sub.SubQueryColumn
 FROM table sub
 WHERE sub.time > main.time
 ORDER BY sub.time asc
 LIMIT 1) SubColumn,
CAST(TIMESTAMP_DIFF((SELECT MIN(sub.time)
FROM table sub
WHERE sub.time > main.time), main.time, SECOND) AS INT64) duration 
FROM table main

но это не сработало. Я получаю ошибку

Коррелированные подзапросы, которые ссылаются на другие таблицы, не поддерживаются, если они не могут быть декоррелированы, например, путем преобразования их в эффективное JOIN.

Могу ли я получить помощь с этим?

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

Ниже для BigQuery Standard SQL

#standardSQL
SELECT event, TIMESTAMP_DIFF(b_time, time, SECOND) duration, b_event
  FROM (
  SELECT event, time,
    LEAD(time) OVER(PARTITION BY grp ORDER BY time) b_time,
    LEAD(event) OVER(PARTITION BY grp ORDER BY time) b_event
  FROM (
    SELECT *, 
      COUNTIF(STARTS_WITH(event, 'A')) OVER(ORDER BY time) grp
    FROM `project.dataset.your_table` t
  )
)
WHERE STARTS_WITH(event, 'A')
-- ORDER BY time

Вы можете протестировать / поиграть с ним, используя фиктивные данные из вашего вопроса, как показано ниже

#standardSQL
WITH `project.dataset.your_table` AS (
  SELECT 'A1' event, TIMESTAMP '2018-01-01 1:00:00' time UNION ALL
  SELECT 'B5', TIMESTAMP '2018-01-01 2:00:00' UNION ALL
  SELECT 'A3', TIMESTAMP '2018-01-01 3:00:00' UNION ALL
  SELECT 'B9', TIMESTAMP '2018-01-01 5:00:00' 
)
SELECT event, TIMESTAMP_DIFF(b_time, time, SECOND) duration, b_event
  FROM (
  SELECT event, time,
    LEAD(time) OVER(PARTITION BY grp ORDER BY time) b_time,
    LEAD(event) OVER(PARTITION BY grp ORDER BY time) b_event
  FROM (
    SELECT *, 
      COUNTIF(STARTS_WITH(event, 'A')) OVER(ORDER BY time) grp
    FROM `project.dataset.your_table` t
  )
)
WHERE STARTS_WITH(event, 'A')
ORDER BY time   

с результатом как

Row event   duration    b_event  
1   A1      3600        B5   
2   A3      7200        B9   

Обратите внимание: приведенное выше решение опирается на утверждение в вашем вопросе - B will always happen after A, поэтому, если у вас есть последовательность, как показано ниже

WITH `project.dataset.your_table` AS (
  SELECT 'A1' event, TIMESTAMP '2018-01-01 1:00:00' time UNION ALL
  SELECT 'A2', TIMESTAMP '2018-01-01 1:30:00' UNION ALL
  SELECT 'B5', TIMESTAMP '2018-01-01 2:00:00' UNION ALL
  SELECT 'A3', TIMESTAMP '2018-01-01 3:00:00' UNION ALL
  SELECT 'B9', TIMESTAMP '2018-01-01 5:00:00' 
)  

результат будет

Row event   duration    b_event  
1   A1      null        null     
2   A2      1800        B5   
3   A3      7200        B9     

Если вам нужно решить эту проблему - попробуйте ниже

#standardSQL
WITH `project.dataset.your_table` AS (
  SELECT 'A1' event, TIMESTAMP '2018-01-01 1:00:00' time UNION ALL
  SELECT 'A2', TIMESTAMP '2018-01-01 1:30:00' UNION ALL
  SELECT 'B5', TIMESTAMP '2018-01-01 2:00:00' UNION ALL
  SELECT 'A3', TIMESTAMP '2018-01-01 3:00:00' UNION ALL
  SELECT 'B9', TIMESTAMP '2018-01-01 5:00:00' 
)
SELECT event, TIMESTAMP_DIFF(b_time, time, SECOND) duration, b_event
FROM (
  SELECT event, time, type, grp,
    FIRST_VALUE(event) OVER(ORDER BY grp RANGE BETWEEN 1 FOLLOWING AND 1 FOLLOWING) b_event,
    FIRST_VALUE(time) OVER(ORDER BY grp RANGE BETWEEN 1 FOLLOWING AND 1 FOLLOWING) b_time
  FROM (
    SELECT event, time, SUBSTR(event, 1, 1) type,
      COUNTIF(STARTS_WITH(event, 'B')) OVER(ORDER BY time) grp
    FROM `project.dataset.your_table` t
  )
)
WHERE STARTS_WITH(event, 'A')
ORDER BY time  

эта версия вернет

Row event   duration    b_event  
1   A1      3600        B5   
2   A2      1800        B5   
3   A3      7200        B9     
0 голосов
/ 08 мая 2018

Вот один из методов:

select m.*,
       timestamp_diff(time, next_b_time, second) as duration
from (select m.*,
             min(case when event like 'B%' then time end) over (order by time desc) as next_b_time
      from main m
     )  m
where event like 'A%';
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...