对 Google Bigquery 中的嵌套字段使用 OFFSET 而不是 UNNEST

Posted

技术标签:

【中文标题】对 Google Bigquery 中的嵌套字段使用 OFFSET 而不是 UNNEST【英文标题】:Using OFFSET instead of UNNEST for nested fields in Google Bigquery 【发布时间】:2020-09-28 15:23:14 【问题描述】:

向 GBQ 专家提问。

这里有两个目的相同的查询

first

SELECT
  fullVisitorId AS userid,
  CONCAT(fullVisitorId, visitStartTime) AS session,
  visitStartTime + (hits[
  OFFSET(0)].time / 1000) AS eventtime,
  date,
  trafficSource.campaign,
  trafficSource.source,
  trafficSource.medium,
  trafficSource.adContent,
  trafficSource.adwordsClickInfo.campaignId,
  geoNetwork.region,
  geoNetwork.city,
  trafficSource.keyword,
  totals.visits AS visits,
  device.deviceCategory AS deviceType,
  hits[OFFSET(0)].eventInfo.eventAction,
  hits[OFFSET(0)].TRANSACTION.transactionId,
  hits[OFFSET(0)].TRANSACTION.transactionRevenue,
  SUBSTR(channelGrouping,0,3) AS newchannelGrouping
FROM
  `some_site.ga_sessions_*`
WHERE
  ARRAY_LENGTH(hits) > 0
  AND _table_suffix BETWEEN '20200201'
  AND '20200201'
  AND fullVisitorId IN (
  SELECT
    DISTINCT(fullVisitorId)
  FROM
    `some_site.ga_sessions_*`,
    UNNEST(hits) AS hits
  WHERE
    _table_suffix BETWEEN '20200201'
    AND '20200201'
    AND (hits.TRANSACTION.transactionId != 'None')
)

second

SELECT
  fullVisitorId AS userid,
  CONCAT(fullVisitorId, visitStartTime) AS session,
  visitStartTime + (hits.time / 1000) AS eventtime,
  date,
  trafficSource.campaign,
  trafficSource.source,
  trafficSource.medium,
  trafficSource.adContent,
  trafficSource.adwordsClickInfo.campaignId,
  geoNetwork.region,
  geoNetwork.city,
  trafficSource.keyword,
  totals.visits AS visits,
  device.deviceCategory AS deviceType,
  hits.eventInfo.eventAction,
  hits.TRANSACTION.transactionId,
  hits.TRANSACTION.transactionRevenue,
  SUBSTR(channelGrouping,0,3) AS newchannelGrouping
FROM
  `some_site.ga_sessions_*`, UNNEST(hits) hits
WHERE
  _table_suffix BETWEEN '20200201' AND '20200201'
  AND fullVisitorId IN (
    SELECT
      DISTINCT(fullVisitorId)
    FROM
      `some_site.ga_sessions_*`,
      UNNEST(hits) AS hits
    WHERE
      _table_suffix BETWEEN '20200201'
      AND '20200201'
      AND (hits.TRANSACTION.transactionId != 'None')
  )

第一个使用OFFSET 从嵌套字段中提取数据。根据执行详情报告,查询需要大约 1.5 MB 的 shuffle。

第二个查询使用UNNEST 来访问嵌套数据。并且混洗字节的数量约为(!)75 MB

两种情况下处理的数据量相同。

现在的问题是:

这是否意味着根据this article(涉及优化插槽之间的通信)我应该使用OFFSET 而不是UNNEST 来获取存储在嵌套字段中的数据?

谢谢!

【问题讨论】:

乍一看,它们并不相同。 OFFSET 示例仅评估 hits 数组的第一个元素,UNNEST 示例公开了更多活动。不明白你在做苹果与橘子的比较。 @shollyman 感谢您的评论。 identical in their purpose - 这就是重点。尽管查询的代码不同,但两个查询都返回相同数量的行和列。我相信我明白OFFSETUNNEST 到底是做什么的。问题是为什么 OFFSET 表现更好 正如@shollyman 提到的,函数并不相同,除了我们得到相同的输出之外,它们的逻辑不同(以不同的方式编码)。我建议阅读官方documentation 和这个article。 @Ines 再次,我明白功能并不相同。我的问题是为什么偏移比 unnest 工作得更快更好。 为一个规范的答案提供了赏金,这让我把所有的声誉都放在了这上面。想知道是否有人可以提供解释 【参考方案1】:

让我们考虑以下使用 BigQuery 公共数据集的示例。

UNNEST - 返回 6 个结果:

WITH t AS (SELECT * FROM `bigquery-public-data.google_analytics_sample.ga_sessions_20170801` WHERE visitId = 1501571504 )
SELECT h FROM t, UNNEST(hits) h

OFFSET - 返回 1 个结果:

WITH t AS (SELECT * FROM `bigquery-public-data.google_analytics_sample.ga_sessions_20170801` WHERE visitId = 1501571504 )
SELECT hits[OFFSET(0)] FROM t

两个查询都引用 GA 公共表中的同一记录。他们表明,使用 UNNEST 连接将在数组中为每个元素带来一行,而使用 OFFSET(0) 将只为数组的第一个元素带来一行。

高数据shuffle不同的原因是因为UNNEST执行了JOIN操作,需要以特定的方式组织数据。 OFFSET 方法只取数组的第一个元素。

【讨论】:

以上是关于对 Google Bigquery 中的嵌套字段使用 OFFSET 而不是 UNNEST的主要内容,如果未能解决你的问题,请参考以下文章

Google BigQuery 中的空嵌套字段

Google BigQuery - 更新嵌套的重复字段

从Google BigQuery中的嵌套表中删除重复项

无法使用 google bigquery(标准)取消嵌套某些字段

Google BIgQuery 中的 POSITION(field) 是啥?

Google BigQuery 选择记录中所有嵌套字段的总和