BigQuery Cross Join 每个都快得多

BigQuery Cross Join Much Faster with Each

我正在尝试按日期查看用户 activity。第一步是使用交叉连接和 where 子句构建自创建用户帐户以来每天的 table。我的第一次尝试是这样的:

SELECT
  u.user_id as user_id,
  date(u.created) as signup_date,
  cal.date as date,

from rsdw.user u
  cross join (select date(dt) as date from [rsdw.calendar] where date(dt) < CURRENT_DATE() ) cal
where
  date(u.created) <= cal.date

(日历 table 只是自 2006 年以来所有日期的列表(3288 行)。用户 table 有约 100 万行。)

此查询需要很长时间...如此之久,以至于我在 1000 秒左右时放弃了它。我尝试稍微调整一下查询。如果我将 "each" 添加到交叉连接:

SELECT
  u.user_id as user_id,
  date(u.created) as signup_date,
  cal.date as date,

from rsdw.user u
  cross join each (select date(dt) as date from [rsdw.calendar] where date(dt) < CURRENT_DATE() ) cal
where
  date(u.created) <= cal.date

我得到一个错误:

Error: Cannot CROSS JOIN two tables with EACH qualifiers.

最后,如果我保留 "each" 但交换 table,只需 90 秒即可完成!

SELECT
  u.user_id as user_id,
  date(u.created) as signup_date,
  cal.date as date,

from (select date(dt) as date from [rsdw.calendar] where date(dt) < CURRENT_DATE() ) cal
  cross join each rsdw.user u
where
  date(u.created) <= cal.date

谁能解释为什么第三次迭代快得多,为什么第二次迭代会导致错误?

基于https://cloud.google.com/bigquery/query-reference,交叉连接甚至不支持"each" 子句,所以我有点惊讶第三个查询甚至运行。

交叉连接形成笛卡尔积(左侧 table 的每条记录都与右侧查询的每条记录连接)——这是一种非常低效的数据连接方式(尽管有时无法避免,例如您尝试创建数据集的方式)。您在用户 table 中有多少条记录? EACH 子句应该只在左边的 table 是较小的(压缩后小于 8MB)时才起作用。您的日历 table 包含 3650 条记录,因此如果您有更多用户,它需要位于左侧 - 就像您在第三个查询中所做的那样。

您遇到了连接如何与子 select 子句交互的极端情况行为。针对命名 table 的连接受益于基于 table 大小的一些优化,而子 select 是不可预测的 table 并且可能导致性能不佳。我已经为我们提交了一个内部错误来改进这个案例。

在第一种较慢的情况下,您的微小 select 日期被复制并广播到少数机器,每台机器处理大量用户。它需要很长时间,因为几乎没有并行性。

第二种情况是由于内部原因导致的查询解析错误,基本上它试图让机器同时处理小范围的日期和小范围的用户,这不会完成交叉连接。

在第三种快速情况下,您的微小 select 日期被复制并广播到许多机器,每台机器处理一小部分用户。由于并行度大,它完成得非常快。

一旦我们完成了我提交的错误,第三种情况的行为有望自动发生。