Google Big Query SQL - получите последнее уникальное значение по дате - PullRequest
0 голосов
/ 08 октября 2018

# РЕДАКТИРОВАТЬ - После комментариев я перефразирую свой вопрос

У меня есть таблица BigQuery, которую я хочу использовать, чтобы получить некоторый KPI своего приложения.В этой таблице я сохраняю каждое создание или обновление как новую строку, чтобы сохранить лучшую историю.Поэтому у меня несколько раз одни и те же данные с другим состоянием.

Пример таблицы :

uuid  |status     |date         
––––––|–––––––––––|––––––––––      
3     |'inactive' |2018-05-12
1     |'active'   |2018-05-10
1     |'inactive' |2018-05-08
2     |'active'   |2018-05-08
3     |'active'   |2018-05-04
2     |'inactive' |2018-04-22
3     |'inactive' |2018-04-18

Мы можем видеть, что у нас есть несколько значений для каждого из данных.

Что я хотел бы получить :

Я хотел бы иметь номер текущей «активной» записи (поэтому не должно быть «неактивной» записи стот же UUID после).И чтобы все усложнить, мне нужно это всего в день .Таким образом, для каждого дня количество «активных» записей, включая записи за предыдущие дни.

Итак, в этом примере у меня должен быть такой результат:

date        | actives
____________|_________
2018-05-02  |   0
2018-05-03  |   0
2018-05-04  |   1
2018-05-05  |   1
2018-05-06  |   1
2018-05-07  |   1
2018-05-08  |   2
2018-05-09  |   2
2018-05-10  |   3
2018-05-11  |   3
2018-05-12  |   2

На самом деле мне удалосьполучите хорошее количество активов за один день. Но моя проблема в том, что мне нужны результаты для каждого дня.

Что я пробовал :

Я застрял с двумя решениямичто каждый из них возвращает свою ошибку.

Первое решение :

WITH
  dates AS(
      SELECT GENERATE_DATE_ARRAY(
          DATE_SUB(CURRENT_DATE(), INTERVAL 6 MONTH), CURRENT_DATE(), INTERVAL 1 DAY)               
      arr_dates )
SELECT
  i_date date,
  (
  SELECT COUNT(uuid)
  FROM (
    SELECT
      uuid, status, date,
      RANK() OVER(PARTITION BY uuid ORDER BY date DESC) rank
    FROM users
    WHERE
      PARSE_DATE("%Y-%m-%d", FORMAT_DATETIME("%Y-%m-%d",date)) <= i_date
  )
  WHERE
    status = 'active'
    and rank = 1
    ## rank is the condition which causes the error
  ) users
FROM
  dates, UNNEST(arr_dates) i_date
ORDER BY i_date;

SELECT с RANK () OVER правильно возвращает пользователей сстолбец ранга, который позволяет мне узнать, какая запись является последней для каждого uuid.Но когда я попробовал это, я получил: Correlated subqueries that reference other tables are not supported unless they can be de-correlated, such as by transforming them into an efficient JOIN. из-за условия rank = 1 .

Второе решение :

WITH
  dates AS(
      SELECT GENERATE_DATE_ARRAY(
          DATE_SUB(CURRENT_DATE(), INTERVAL 6 MONTH), CURRENT_DATE(), INTERVAL 1 DAY)               
      arr_dates )
SELECT
  i_date date,
  (
  SELECT
    COUNT(t1.uuid)
  FROM
    users t1
  WHERE
    t1.date = (
      SELECT MAX(t2.date)
      FROM users t2
      WHERE
        t2.uuid = t1.uuid
        ## Here that's the i_date condition which causes problem 
        AND PARSE_DATE("%Y-%m-%d", FORMAT_DATETIME("%Y-%m-%d", t2.date)) <= i_date 
    )
    AND status='active' ) users
FROM
  dates,
  UNNEST(arr_dates) i_date
ORDER BY i_date;

Здесь также работает второй выбор, который корректно возвращает количество активных пользователей за текущий день.Но проблема заключается в , когда я пытаюсь использовать i_date для получения данных за несколько дней.И здесь я получил LEFT OUTER JOIN cannot be used without a condition that is an equality of fields from both sides of the join. ошибку ...

Какое решение более успешно?Что я должен изменить?

И, если мой способ хранения данных не подходит, как мне поступить, чтобы сохранить точную историю?

Ответы [ 2 ]

0 голосов
/ 08 октября 2018

Я думаю, что это - или что-то подобное - будет делать то, что вы хотите:

SELECT day,
       coalesce(running_actives, 0) - coalesce(running_inactives, 0)
FROM UNNEST(GENERATE_DATE_ARRAY(DATE('2015-05-11'), DATE('2018-06-29'), INTERVAL 1 DAY)
           ) AS day left join
     (select date, sum(countif(status = 'active')) over (order by date) as running_actives,
             sum(countif(status = 'active')) over (order by date) as running_inactives
      from t
      group by date
     ) a
     on a.date = day
order by day;

Точное решение зависит от того, будет ли "неактивный" включать день (как указано выше) или вступит в силуНа следующий день.Любой из них обрабатывается одинаково, с использованием кумулятивных сумм активов и неактивов, а затем с учетом разницы.

Чтобы получить данные за все дни, генерируются дни с использованием массивов и unnest().Если у вас есть данные за все дни, этот шаг может быть ненужным

0 голосов
/ 08 октября 2018

Ниже для BigQuery Standard SQL

#standardSQL
SELECT date, COUNT(DISTINCT uuid) total_active 
FROM `project.dataset.table`
WHERE status = 'active'
GROUP BY date 
-- ORDER BY date   

Обновление для решения вашего "перефразированного" вопроса: o)
Ниже приведен пример использования фиктивных данных из вашего вопроса

#standardSQL
WITH `project.dataset.users` AS (
  SELECT 3 uuid, 'inactive' status, DATE '2018-05-12' date UNION ALL
  SELECT 1, 'active', '2018-05-10' UNION ALL
  SELECT 1, 'inactive', '2018-05-08' UNION ALL
  SELECT 2, 'active', '2018-05-08' UNION ALL
  SELECT 3, 'active', '2018-05-04' UNION ALL
  SELECT 2, 'inactive', '2018-04-22' UNION ALL
  SELECT 3, 'inactive', '2018-04-18' 
), dates AS (
  SELECT day FROM UNNEST((
    SELECT GENERATE_DATE_ARRAY(MIN(date), MAX(date))
    FROM `project.dataset.users`
  )) day
), active_users AS (
  SELECT uuid, status, date first, DATE_SUB(next_status.date, INTERVAL 1 DAY) last FROM (
    SELECT uuid, date, status, LEAD(STRUCT(status, date)) OVER(PARTITION BY uuid ORDER BY date ) next_status
    FROM `project.dataset.users` u
  )
  WHERE status = 'active'
)
SELECT day, COUNT(DISTINCT uuid) actives
FROM dates d JOIN active_users u
ON day BETWEEN first AND IFNULL(last, day)
GROUP BY day 
-- ORDER BY day

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

Row day         actives  
1   2018-05-04  1    
2   2018-05-05  1    
3   2018-05-06  1    
4   2018-05-07  1    
5   2018-05-08  2    
6   2018-05-09  2    
7   2018-05-10  3    
8   2018-05-11  3    
9   2018-05-12  2    
...