Функция анализа окна, где оконная рама и порядок - это разные поля? - PullRequest
1 голос
/ 15 мая 2019

Как вычислить среднюю продолжительность записей, у которых end_date равен 1 часу до этой записи start_date?

Я могу сделать это с помощью самостоятельного соединения:

  SELECT AVG(p.duration) AS prior_duration
  FROM `bigquery-public-data`.london_bicycles.cycle_hire c
  JOIN `bigquery-public-data`.london_bicycles.cycle_hire p
  ON c.start_station_id = p.start_station_id AND
     p.end_date BETWEEN TIMESTAMP_SUB(c.start_date, INTERVAL 3600 SECOND)
                  AND c.start_date

но как я могу сделать это более эффективно (без самостоятельного объединения)? что-то вроде:

AVG(duration)
         OVER(PARTITION BY start_station_id
         ORDER BY UNIX_SECONDS(end_date) ASC 
         RANGE BETWEEN 3600 PRECEDING AND CURRENT ROW) AS prior_duration

но использует начальную дату текущих записей.

Ответы [ 2 ]

2 голосов
/ 15 мая 2019

ОБНОВЛЕНИЕ: см. Комментарий Михаила.это не работаетЯ обновил запрос, чтобы BigQuery не проводил быструю оптимизацию.

Вот точное решение.Идея состоит в том, чтобы построить массив всех записей на станции и отфильтровать за прошедший час, используя коррелированный запрос.Обработка всего набора данных заняла 7 секунд.

Размер массива записей на станции должен быть <100 МБ.Сгруппируйте столько полей, сколько необходимо, чтобы сделать ваши массивы достаточно маленькими:) </p>

WITH all_hires AS (
  SELECT 
    start_station_id
    , ARRAY_AGG(STRUCT(duration, 
                       start_date, 
                       TIMESTAMP_SUB(start_date, INTERVAL 1 HOUR) AS start_date_m1h, 
                       end_date)) AS hires
  FROM `bigquery-public-data`.london_bicycles.cycle_hire
  GROUP BY start_station_id
),

hires_by_ts AS (
  SELECT
    start_station_id
    , h.start_date
    , (SELECT AVG(duration) FROM UNNEST(hires) 
       WHERE end_date BETWEEN h.start_date_m1h AND h.start_date)
         AS duration_prev_hour
    , (SELECT COUNT(duration) FROM UNNEST(hires) 
       WHERE end_date BETWEEN h.start_date_m1h AND h.start_date)
         AS numreturns_prev_hour
  FROM
    all_hires, UNNEST(hires) AS h
)

SELECT * from hires_by_ts
WHERE duration_prev_hour IS NOT NULL
ORDER BY duration_prev_hour DESC
LIMIT 5
1 голос
/ 17 мая 2019

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

WITH cycle_hires AS (
  SELECT 
    start_station_id,
    start_date,
    ARRAY_AGG(STRUCT(end_date, duration)) OVER (
      PARTITION BY start_station_id
      ORDER BY end_date ASC
      ROWS BETWEEN 100 PRECEDING AND CURRENT ROW
    ) AS previous
  FROM `bigquery-public-data`.london_bicycles.cycle_hire AS c
)
SELECT
  c.start_station_id,
  AVG(p.duration) AS previous_duration,
  COUNT(*) AS number_of_previous_trips_used
FROM cycle_hires AS c
  JOIN UNNEST(previous) AS p
  WHERE p.end_date BETWEEN TIMESTAMP_SUB(c.start_date, INTERVAL 3600 SECOND) AND c.start_date
GROUP BY 1

С этим набором данных (~ 24M строк) использование до 100 предыдущих строк займет ~ 20 с, а до 1000 предыдущих строк - ~ 120 с.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...