BigQuery - 时间序列和选择“最新”记录的最有效方式

时间:2017-07-14 16:31:56

标签: google-bigquery

在我们的BQ设计中,我们有一个客户表(嵌套的原始数据),它来自我们的微服务层(使用kinesis steams),其中每个事件都有事件所针对的实体的最新实体快照(后处理)改变后的形象)。我想是一种现代变化数据捕获。

每个事件中的最新快照是我们如何填充BigQuery - 它是以APPEND ONLY模式提取并加载到biqQuery(通过apache spark结构化蒸汽连接器)。 (这与给定ID的变异和更新一行不同)

所以鉴于这只是追加,这个表的大小当然会随着时间的推移而增长 - 一个事件每次更改的条目。然而,很好的是客户状态和变更的完整时间序列(我们的要求),并且是不可变的。我们可以通过重播事件来重建完整的仓库,例如....在上下文中足够了。

这样做的一个结果是加载到BigQuery可能会导致重复(例如,如果火花错误并重试微批,BQ在作业加载时不是幂等结构化的流式接收器,或者仅仅由于分布式性质,它通常可能)。 SteamingInserts可能会在以后查看,因为它有助于重复数据删除.....

这种架构的结果是我需要在原始时间序列数据的顶部查看(记住偶尔会有重复),在这些条件下返回LATEST记录。

最新值由客户记录(metadata.lastUpdated)上的元数据结构字段确定 - 带MAX的行(metadata.lastUpdated)是最新的。这是由我们的MS层保证的。

这也是一个真实的事件时间戳。表ID DAY已分区,并且有一个_PARTITIONTIME列,但这只是一个摄取时间,我无法使用它。当我可以指定要用作分区时间的列时,请大家好! (心愿)。

副本将是两行,其中SAME客户'id'和'metadata.lastUpdated' - 所以MAX(metadata.lastUpdated)可以返回2行,所以我需要使用

ROW_NUMBER()OVER(PARTITION BY ....所以我可以选择rowNum = 1

在我看来,也只选择有重复的1行。

好的话语/上下文(对不起),下面是我查看SQL以获取最新信息。它适用于我的测试,但我不确定当表格/行数变大时,它是实现我的结果的最有效方式,并且想知道是否有任何BigQuery boffins可能有更高效/聪明SQL这样做?为什么SQL没问题,但绝不是性能调优方面的专家,特别是为BQ性能调优执行SQL的最佳方法。

我只是希望能够将所有数据放在一个表中并依赖dremel引擎的强大功能来查询它,而不是需要有多个表或者做任何太复杂的数据。

所以我的SQL在下面。注意 - 我的时间戳是以字符串形式提取的,因此也需要在视图中对此进行PARSE。

WITH
  cus_latest_watermark AS (
  SELECT
    id,
    MAX(PARSE_TIMESTAMP("%Y-%m-%dT%H:%M:%E*S%Ez", metadata.lastUpdated)) AS maxLastUpdatedTimestampUTC
  FROM
    `project.dataset.customer_refdata`
  GROUP BY
    id ),
  cust_latest_rec_dup AS (
  SELECT
    cus.*,
    ROW_NUMBER() OVER (PARTITION BY cus.id ORDER BY cus.id) AS rowNum
  FROM
    `project.dataset.customer_refdata` cus
  JOIN
    cus_latest_watermark
  ON
    cus.id = cus_latest_watermark.id
    AND PARSE_TIMESTAMP("%Y-%m-%dT%H:%M:%E*S%Ez", cus.metadata.lastUpdated) = cus_latest_watermark.maxLastUpdatedTimestampUTC)
SELECT
  cust_latest_rec_dup.* EXCEPT(rowNum)
FROM
  cust_latest_rec_dup
WHERE
  rowNum = 1

谢谢!

2 个答案:

答案 0 :(得分:4)

在下面尝试BigQuery Standard SQL

  
#standardSQL
WITH cus_watermark AS (
  SELECT
    *,
    PARSE_TIMESTAMP("%Y-%m-%dT%H:%M:%E*S%Ez", metadata.lastUpdated) AS UpdatedTimestampUTC
  FROM `project.dataset.customer_refdata`
),
cust_latest_rec_dup AS (
  SELECT 
    *,
    ROW_NUMBER() OVER (PARTITION BY id ORDER BY UpdatedTimestampUTC DESC) AS rowNum
  FROM cus_watermark
)
SELECT * EXCEPT(rowNum)
FROM cust_latest_rec_dup
WHERE rowNum = 1  

您可以使用以下虚拟数据播放/测试此方法

#standardSQL
WITH `project.dataset.customer_refdata` AS (
  SELECT 1 AS id, '2017-07-14 16:47:27' AS lastUpdated UNION ALL
  SELECT 1, '2017-07-14 16:47:27' UNION ALL
  SELECT 1, '2017-07-14 17:47:27' UNION ALL
  SELECT 1, '2017-07-14 18:47:27' UNION ALL
  SELECT 2, '2017-07-14 16:57:27' UNION ALL
  SELECT 2, '2017-07-14 17:57:27' UNION ALL
  SELECT 2, '2017-07-14 18:57:27' 
),
cus_watermark AS (
  SELECT
    *,
    PARSE_TIMESTAMP("%Y-%m-%d %T", lastUpdated) AS UpdatedTimestampUTC
  FROM `project.dataset.customer_refdata`
),
cust_latest_rec_dup AS (
  SELECT 
    *,
    ROW_NUMBER() OVER (PARTITION BY id ORDER BY UpdatedTimestampUTC DESC) AS rowNum
  FROM cus_watermark
)
SELECT * EXCEPT(rowNum)
FROM cust_latest_rec_dup
WHERE rowNum = 1

答案 1 :(得分:4)

我是米哈伊尔的忠实粉丝,我们长期以来一直在做OVER(ORDER)这样的查询 - 但是让我提出一个替代方案,感谢#standardSQL。

此查询失败,因为它在单个分区中的ORDER BY元素太多了:

#standardSQL
SELECT *
FROM (
  SELECT repo.name, type, actor.id as actor, payload, created_at
    , ROW_NUMBER() OVER(PARTITION BY actor.id ORDER BY created_at DESC) rn
  FROM `githubarchive.month.201706` 
)
WHERE rn=1
ORDER BY created_at
LIMIT 100


"Error: Resources exceeded during query execution."

同时此查询在15秒内运行:

#standardSQL
SELECT actor, event
FROM (
  SELECT actor.id actor, 
    ARRAY_AGG(
      STRUCT(type, repo.name, payload, created_at) 
      ORDER BY created_at DESC LIMIT 1
    ) events
  FROM `githubarchive.month.201706` 
  GROUP BY 1
), UNNEST(events) event
ORDER BY event.created_at
LIMIT 100

这是因为允许ORDER BY删除每个GROUP BY上的所有内容 - 除了顶部记录。

如果你想要所有记录,相当于SELECT *(感谢Elliott):

#standardSQL
SELECT event.* FROM (
  SELECT ARRAY_AGG(
    t ORDER BY t.created_at DESC LIMIT 1
  )[OFFSET(0)]  event
  FROM `githubarchive.month.201706` t 
  GROUP BY actor.id
)
ORDER BY created_at
LIMIT 100