【问题标题】:Google BigQuery Standard SQL - Sales Sliding WindowGoogle BigQuery 标准 SQL - 销售滑动窗口
【发布时间】:2017-11-12 07:57:44
【问题描述】:

我想知道如何最好地在 BigQuery standardSQL(不是旧版)中同时实现翻转和滑动窗口。

Apache Spark 让这变得非常简单 - 对于长度为 10 分钟的“滑动”窗口,每 5 分钟滑动一次,groupBy(window("10 minutes","5 minutes)) 和滚动窗口 groupBy(window("10分钟”))。

考虑我有一个简化的订单行:

orderId, 
orderPlacedTimestamp, 
orderTotals.grandTotalNet, 
orderTotals.grandTotalGross 

(注意总数的结构)

我需要两个总订单销售价值:

  1. 每小时翻滚的销售窗口
  2. 滑动 10 MINUTE / 5 MINUTE 窗口(如上所述)。

我开始使用 SQL over,但在分区、时间戳和获取正确的开始和结束窗口方面有点混乱。对于每个结果集应该是:

windowStartTime, 
windowEndTime, 
windowTotalAmount

所以窗口有一个开始和结束时间(这对于滑动至关重要,因为窗口 10 的长度与滑动持续时间 5 不同,因此给定的行可以在多个窗口中)。

如何在 BigQuery 中执行此操作?

使用当前 SQL 更新 11/06:

SELECT 
  TIMESTAMP_SECONDS(FIRST_VALUE(ts_5min*5*60) OVER(w)) as startWindowTime,
  TIMESTAMP_SECONDS(LAST_VALUE(ts_5min*5*60) OVER(w)) as endWindowTime,
  SUM(orderTotalNetConverted) OVER(ORDER BY ts_5min RANGE BETWEEN 1 PRECEDING AND CURRENT ROW) as windowSalesTotal
FROM (
  SELECT 
    CAST(UNIX_SECONDS(TIMESTAMP_TRUNC(orderPlacedTimestamp, MINUTE))/60/5 AS INT64)
      AS ts_5min,
    orderTotalNetConverted
  FROM orders
)
WINDOW w AS (ORDER BY ts_5min RANGE BETWEEN 1 PRECEDING AND CURRENT ROW)
ORDER BY startWindowTime desc

【问题讨论】:

  • 请编辑并添加您目前编写的 SQL

标签: google-bigquery


【解决方案1】:

翻滚窗口更容易做到,它只是每小时间隔的常规GROUP BY

SELECT 
  TIMESTAMP_TRUNC(orderPlacedTimestamp, HOUR), 
  SUM(orderTotals.grandTotalNet)
FROM T
GROUP BY 1

对于滑动窗口,我会首先使用以下方法将时间戳标准化为 5 分钟间隔:

  1. TIMESTAMP_TRUNC(orderPlacedTimestamp, MINUTE) 到达分钟边界
  2. UNIX_SECONDS 转换为纪元以来的秒数
  3. 除以 60 得到分钟
  4. 除以 5 得到 5 分钟间隔
  5. 四舍五入到整数:

CAST(UNIX_SECONDS(TIMESTAMP_TRUNC(orderPlacedTimestamp, MINUTE))/60/5 AS INT64)

现在您可以使用标准的OVER() 子句来获得10 分钟的窗口,这意味着一次2 个这样的间隔,为了获得开始时间,请使用FIRST_VALUE 解析函数:

SELECT
  orderId,
  TIMESTAMP_SECONDS(FIRST_VALUE(ts_5min*5*60) OVER(w)) startWindowTime,
  TIMESTAMP_ADD(TIMESTAMP_SECONDS(FIRST_VALUE(ts_5min*5*60) OVER(w)),
                INTERVAL 10 MINUTE) endWindowTime,
  SUM(grandTotalNet) OVER(w)
FROM (
  SELECT 
  *,
  CAST(UNIX_SECONDS(TIMESTAMP_TRUNC(orderPlacedTimestamp, MINUTE))/60/5 AS INT64) 
    AS ts_5min
  FROM t
)
WINDOW w AS (ORDER BY ts_5min RANGE BETWEEN 1 PRECEDING AND CURRENT ROW)

【讨论】:

  • 谢谢 mosha 我会试一试!那么如何获得滑动窗口中每一行的“startWindowTime”和“endWindowTime”?例如 start = 2017-06-06 14:00:00....end = 2017-06-06 14:10:00 对于每一行?
  • 感谢 Mosha - 几件事。 1.我尝试添加结束窗口时间以及:TIMESTAMP_SECONDS(LAST_VALUE(ts_5min * 5 * 60)OVER(w))作为endWindowTime,使用'lastvalue',但结束窗口是否相同?我做错了什么? 2. 我似乎没有看到任何 :05 幻灯片....例如第一个窗口 12:00-12:10,第二个窗口是 12:05-12:15(滑动 5,仍然长度为 10).. ...您自己的本地测试是否产生了 :05 的开始时间?我会用我的代码更新我的原始帖子......谢谢一百万!
  • 想知道是否可以通过制作 UDF 让用户更简单?你有这方面的经验吗?也许一旦我们了解了核心 SQL,我就可以看看一些东西。与说 spark 相比,这有点复杂,更难阅读,即使它确实有效(这当然是主要的 :)....cherers mate!
  • 哦,我撤回了第二条评论,我很抱歉,有些窗口以 :05 开头,对不起!结果集中只有好几页了:)
  • 我更新了答案以添加 endWindowTime (开始时间后仅 10 分钟)。我不相信 UDF 可以在这里使用,因为它们不适用于行组。 TVF 本来可以工作,但 BigQuery 还不支持它们。我同意,对于这个问题,SQL 比 Spark 更难阅读
【解决方案2】:

下面是sliding 10 MINUTE / 5 MINUTE window

#standardSQL
WITH starts AS (
  SELECT TIMESTAMP_ADD(TIMESTAMP_TRUNC(first, HOUR), INTERVAL step MINUTE) AS start
  FROM 
    (SELECT MIN(orderPlacedTimestamp) AS first, MAX(orderPlacedTimestamp) AS last FROM YourTable), 
    UNNEST(GENERATE_ARRAY(0, TIMESTAMP_DIFF(last, TIMESTAMP_TRUNC(first, HOUR), MINUTE) , 5)) AS step
)
SELECT 
  start, 
  SUM(orderTotals.grandTotalNet) AS net, 
  SUM(orderTotals.grandTotalGross) AS gross
FROM starts AS s JOIN YourTable AS t
ON t.orderPlacedTimestamp BETWEEN s.start AND TIMESTAMP_ADD(start, INTERVAL 10 MINUTE)
GROUP BY start
ORDER BY start 

正如 Mosha 在他的回答中提到的 - 翻滚 HOURLY 窗口很容易 - 但如果您需要不同的窗口侧 - 上述方法更灵活(我认为)并且易于调整任何窗口大小,通过更改相应的 5 键入和10 查询代码
但总的来说 - 想提一下 - 使用解析函数比加入更优化

【讨论】:

  • 感谢米哈伊尔!这是另一个不错的选择——性能是否足以保证坚持使用分析函数——我们的数据集并不庞大。非常感谢您抽出宝贵时间!
  • 真的取决于 - 双方各有利弊 - 我认为取决于具体情况。当然,主要标准 - 无论你有什么选择 - 它至少必须按预期工作 - 如果两者都工作 - 测试性能并做出决定:o)
  • 米哈伊尔,作为一个大师,你能帮我做一些时间戳管理 - stackoverflow.com/questions/44603021/…
猜你喜欢
  • 2014-04-20
  • 2015-10-11
  • 1970-01-01
  • 2015-02-17
  • 1970-01-01
  • 1970-01-01
  • 2022-08-19
  • 1970-01-01
相关资源
最近更新 更多