Один из способов сделать это - это самостоятельное соединение с той же таблицей и задержкой в 1 месяц. Таким образом, мы сопоставляем комбинации пользователя и месяца с пользователем и предыдущим месяцем, чтобы определить, является ли он возвращающимся пользователем. Например, используя строку 2M publi c table bigquery-public-data.hacker_news.stories
и конкретного пользователя:
![enter image description here](https://i.stack.imgur.com/xrsyV.png)
Обратите внимание, что prev_month
равно null (мы использовали LEFT OUTER JOIN
) для 2014-02-01 , поскольку пользователь был не активным в течение 2014-01-01 . Мы присоединяемся к авторам и месяцам с задержкой:
FROM authors AS a
LEFT OUTER JOIN authors AS b
ON a.author = b.author
AND a.month = DATE_ADD(b.month, INTERVAL 1 MONTH)
Затем мы считаем пользователя повторяющимся, если предыдущий месяц не был нулевым:
COUNT(a.author) AS num_users,
COUNTIF(b.month IS NOT NULL) AS num_returning_users
Обратите внимание, что мы не используем DISTINCT
здесь, как мы уже сгруппировали по комбинациям автора и месяца при определении orders
в качестве CTE. Возможно, вам придется принять это во внимание для других примеров.
Полный запрос:
WITH
authors AS (
SELECT
author,
DATE_TRUNC(DATE(time_ts), MONTH) AS month
FROM
`bigquery-public-data.hacker_news.stories`
WHERE
author IS NOT NULL
GROUP BY 1,2)
SELECT
*,
ROUND(100*SAFE_DIVIDE(num_returning_users,
num_users),2) AS retention
FROM (
SELECT
a.month,
COUNT(a.author) AS num_users,
COUNTIF(b.month IS NOT NULL) AS num_returning_users
FROM
authors AS a
LEFT OUTER JOIN
authors AS b
ON
a.author = b.author
AND a.month = DATE_ADD(b.month, INTERVAL 1 MONTH)
GROUP BY 1
ORDER BY 1
LIMIT 100)
и фрагмент результатов:
![enter image description here](https://i.stack.imgur.com/VsKzS.png)
, которые являются правильными результатами, т. Е. Для 2007-03-01
:
![enter image description here](https://i.stack.imgur.com/hlisT.png)
Производительность не слишком велика, но в этом случае мы выбираем только поля, необходимые для агрегированных данных, поэтому отсканированные данные невелики, а время выполнения не слишком велико (~ 5 с).
Альтернативой является использование EXISTS()
внутри COUNTIF()
вместо объединения, но у меня это занимает больше времени (~ 7 с). Запрос