在我们的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
谢谢!
答案 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