Google bigquery BigQuery:如何计算每天和类别的不同访客的运行计数?

Google bigquery BigQuery:如何计算每天和类别的不同访客的运行计数?,google-bigquery,Google Bigquery,在Google BigQuery中,我有一个如下表: 开始时间:字符串,访问者ID:字符串,类别:字符串 此内容的示例: startTime visitorId category ------------------- --------- -------- 2013-11-27 00:00:00 A X 2013-11-27 05:00:00 A X 2013-11-27 07:00:00

在Google BigQuery中,我有一个如下表:

开始时间:字符串,访问者ID:字符串,类别:字符串

此内容的示例:

startTime            visitorId   category
-------------------  ---------   --------
2013-11-27 00:00:00     A           X         
2013-11-27 05:00:00     A           X 
2013-11-27 07:00:00     B           X 
2013-11-28 08:00:00     C           X 
我希望得到以下结果:

day         category  runningCountOfDistinctVisitors  
---------   --------  ------------------------------   
2013-11-27     X                   2
2013-11-28     X                   3
我尝试了以下查询,但似乎不起作用(它已在1.2M行表上运行了3个多小时,但仍未完成):

选择左侧(a.startTime,10)作为日期,
a、 类别,
将(不同的a.visitorId)计数为不同访问者的运行计数
从[MyDataset.MyTable]a
左键连接a.category=b.category上的每个[MyDataset.MyTable]b
左(b.startTime,10)<左(a.startTime,10)
按类别、日期分组
按类别、日期排序

我还尝试使用分区函数,但似乎不支持count distinct

countdistinct是一个计算密集型操作(这就是为什么BigQuery提供在1000之后进行近似计数,除非明确要求不这样做)。进行几乎交叉连接也是一项密集的操作。将这两种方法与大型数据集混合使用,您可能会遇到计算上难以解决的问题

建议(因为我无法访问您的数据):

  • 使用每个组执行子查询,而不是使用不同的计数。然后在一个外部查询中计算。同样的结果,可能有更好的计算分布
  • 为什么要让每个人都加入,而不是只加入每个人
更新:我喜欢Radek的答案,他使用COUNT()OVER()而不是JOIN:

试试这个:

ts:时间戳,访问者:字符串,类别:字符串

ts                       visitor  category
-----------------------  -------  --------
2013-11-27 00:00:00 UTC  A        X  
2013-11-27 00:00:00 UTC  A        X  
2013-11-27 00:00:00 UTC  B        X  
2013-11-28 00:00:00 UTC  C        X  
2013-11-27 00:00:00 UTC  A        Y  
2013-11-28 00:00:00 UTC  B        Y  
2013-11-29 00:00:00 UTC  C        Y
查询:

select 
  day, category, sum(cd) 
over
  (partition by category order by day) as running_total
from (select date(ts) as day, category, count(distinct visitor) as cd from
  [test.runningtotal] group by day, category)
这将产生:

day         category  running_total
----------  --------  -------------
2013-11-27  X         2  
2013-11-28  X         3  
2013-11-27  Y         1  
2013-11-28  Y         2  
2013-11-29  Y         3

我没有在大型数据集上测试这一点,但它可能比JOIN解决方案快。

意识到我做这件事已经很晚了,但它帮助我解决了一些我正在做的事情,所以我想我应该再添加一点

在BigQuery中,可以对随窗口变宽的数据集运行滚动计数

在这个例子中,它看起来像这样

`SELECT day, category, MAX(runningCountofDistinctVisitors) running_ct
FROM
(SELECT left(a.startTime,10) as day, 
a.category category,
count(distinct a.visitorId)
  OVER(PARTITION BY category
  ORDER BY LEFT(a.startTime,10)) as runningCountOfDistinctVisitors
FROM [MyDataset.MyTable] a 
LEFT JOIN EACH [MyDataset.MyTable] b ON a.category = b.category 
WHERE left(b.startTime,10) < left(a.startTime,10)
GROUP EACH BY a.category, day, a.visitorId)
GROUP EACH BY day, category
ORDER EACH BY day, category`
`选择日期、类别、最大运行次数(不同访问者的运行次数)\u ct
从…起
(选择左侧(a.startTime,10)作为日期,
a、 类别,
计数(不同的a.visitorId)
超过(按类别划分)
按左(a.startTime,10))排序,作为不同访问者的运行计数
从[MyDataset.MyTable]a
左键连接a.category=b.category上的每个[MyDataset.MyTable]b
左(b.startTime,10)<左(a.startTime,10)
按a.类别、日期、a.就诊者分组)
按天分组,分类
按天、类别订购`
这解决了计数高于预期值的问题,因为窗口正在扩大,以包括当天和所有前几天,而不是将前几天的计数相加


我相信也有一种方法可以做到这一点,而不需要每天都获取最大值的外部查询,但我还没有弄清楚这一点。

另一种方法是使用

row_number() over (partition by visitor,category order by cast(ts as date)) as row_n 
然后

sum(if(row_n=1,1,0)) over (partition by category order by cast(ts as date)

以前的一些答案并不能真正计算出几天内的差异。他们只是按天计算不同的数字,然后将这些数字相加

这组查询将允许您在非常大的数据集上计算运行计数差异。我已经在数十亿个独特的ID上进行了测试

它将大量独特的访客ID压缩到小草图中,每天一个,分类一个,可以与其他草图合并以快速获得独特的计数

它使用临时表,因为它们比CTE或子查询快得多

-- create one sketch per <day, category> pair
CREATE TEMP TABLE sketches AS
(SELECT
  category
, DATE(ts) day
, HLL_COUNT.INIT(visitor_id, 24) visitor_sketch
FROM [MyDataset.MyTable]
WHERE DATE(ts) >= DATE_SUB(CURRENT_DATE, INTERVAL 5 DAY)
GROUP BY category, day
);

-- get a list of the days involved in the study
CREATE TEMP TABLE window_starts AS
(
  SELECT DISTINCT(day) day
  FROM sketches
);

/* For each category and day in the study, merge all the 
   corresponding sketches for that category and from the
   beginning of the study up to that day.

   N.B. "MERGE" performs an approximate count distinct
   see https://cloud.google.com/bigquery/docs/reference/standard-sql/hll_functions#hll_countmerge
*/
SELECT
  category
, window_starts.day
, HLL_COUNT.MERGE(visitor_sketch) visitors
FROM window_starts
CROSS JOIN sketches
WHERE sketches.day <= window_starts.day
GROUP BY category, window_starts.day
ORDER BY 1
--每对创建一个草图
将临时表草图创建为
(选择
类别
,日期(ts)日
,HLL_COUNT.INIT(访客id,24)访客草图
来自[MyDataset.MyTable]
其中日期(ts)>=日期子项(当前日期,间隔5天)
按类别、日期分组
);
--获取参与研究的天数列表
创建临时表窗口\u开始时为
(
选择不同的(日)日
根据草图
);
/*对于研究中的每个类别和日期,合并所有
该类别的对应草图以及
从学习开始到那天。
注意,“合并”执行近似计数
看见https://cloud.google.com/bigquery/docs/reference/standard-sql/hll_functions#hll_countmerge
*/
挑选
类别
,开窗日
,HLL\U计数。合并(访客\U草图)访客
从窗口开始
交叉连接草图

WHERE sketches.day注意:我总共有3个查询当前标记为“queryrunning”(包括我提到的查询)。这些查询是3个多小时前开始的。这些问题都很相似。非常感谢您的回答!我已经试过了,但是由于某种原因,跑步的总成绩比应该的要高一点(在我的桌子上)。我将尝试深入挖掘,以找出原因。count(distinct visitor)是近似值,try count(distinct visitor,xxx)其中xxx是一个较高的数字,高于我忘记提到的日期/类别组应有的(参见计数),是的,我已经尝试将count distinct与第二个(可选)参数一起使用,但它也不起作用。我将尝试看看是否可以将我的表与一些数据一起公开。再次感谢您的帮助,非常感谢!如果访问者对同一类别重复几天,这将不起作用。
-- create one sketch per <day, category> pair
CREATE TEMP TABLE sketches AS
(SELECT
  category
, DATE(ts) day
, HLL_COUNT.INIT(visitor_id, 24) visitor_sketch
FROM [MyDataset.MyTable]
WHERE DATE(ts) >= DATE_SUB(CURRENT_DATE, INTERVAL 5 DAY)
GROUP BY category, day
);

-- get a list of the days involved in the study
CREATE TEMP TABLE window_starts AS
(
  SELECT DISTINCT(day) day
  FROM sketches
);

/* For each category and day in the study, merge all the 
   corresponding sketches for that category and from the
   beginning of the study up to that day.

   N.B. "MERGE" performs an approximate count distinct
   see https://cloud.google.com/bigquery/docs/reference/standard-sql/hll_functions#hll_countmerge
*/
SELECT
  category
, window_starts.day
, HLL_COUNT.MERGE(visitor_sketch) visitors
FROM window_starts
CROSS JOIN sketches
WHERE sketches.day <= window_starts.day
GROUP BY category, window_starts.day
ORDER BY 1