【问题标题】:BigQuery GROUP BY vs. PARTITION BY when appending the auxiliary columns附加辅助列时的 BigQuery GROUP BY 与 PARTITION BY
【发布时间】:2021-09-08 13:16:07
【问题描述】:

为了获取主键的补充信息,我想出了两种方法。

一种是使用GROUP BYaggregation function [1],另一种是使用PARTITION BYwindow/analytic function (~ row_number) [2]。

我怀疑GROUP BY 是由于聚合函数而导致性能较低的方式(可能不是真的,因为PARTITION BY 方式使用子查询),但是,我不确定哪种方式是附加补充的更好方式列。

  1. 就时间复杂度和性能而言,哪种方法更好?
  2. 另外,有没有更好的附加辅助列的方法?
示例案例:获取用户上次登录信息(如ip,country_code,...)

可能的解决方案 1:GROUP BY 带有聚合函数

#standardSQL
SELECT
    user_id,  -- primary key
    ARRAY_AGG(
        STRUCT(
            ip, country_code, os, channel_type
        )
        ORDER BY login_timestamp DESC LIMIT 1
    )[OFFSET(0)].*
FROM user_login_info_table
GROUP BY user_id

可能的解决方案 2:PARTITION BY 带有子查询

#standardSQL
WITH
login_log AS (
    SELECT
        user_id,
        ip, country_code, os, channel_type,
        ROW_NUMBER() OVER (
            PARTITION BY user_id ORDER BY login_timestamp DESC
        ) AS rn
    FROM user_login_info_table
)
SELECT user_id, ip, country_code, os, channel_type
FROM login_log
WHERE rn = 1

参考上面的例子是一个过于简化的例子,它忽略了其他聚合字段。

参考文献

【问题讨论】:

  • GROUP BY 需要与需要聚合的其他字段 一起使用时,我最终使用solution 1,而当不需要其他聚合时使用solution 2。多亏了这个建议,我最终通过执行样本来选择具有经验结果的查询。

标签: google-bigquery


【解决方案1】:

#2 的“正式”改进:

SELECT
    user_id,
    ip, country_code, os, channel_type
FROM user_login_info_table
WHERE true
QUALIFY 1 = ROW_NUMBER() OVER (
        PARTITION BY user_id ORDER BY login_timestamp DESC)     

哪个更好

在您的 oversimplified example 中 - 第二种解决方案对我来说更有意义 - 但在更实际的用例中它取决于
您始终可以针对您的数据运行这两个版本并查看执行计划/详细信息以了解每个解决方案的成本

【讨论】:

【解决方案2】:

我测试了多个场景并最终得出以下结论。

GROUP BY 在很多情况下通常会减少使用aggregate functions 返回的行数。 [1] 另一方面,PARTITION BY 使用 WINDOW or ANALYTIC functions 返回相同数量的行。 因此,当可以通过特定键收集结果并减少输出行时,这是一个优势。但是,当难以使用 GROUP BY 键收集行时,PARTITION BY 被认为作为开销更有利。

但是,正如上面答案中提到的,最好对照数据进行测试。

示例 1:
使用聚合函数,可以减少结果→GROUP BY

SELECT
    user_id,
    COUNT(DISTINCT logkey) as login_freq,  -- Agg. Function
    ARRAY_AGG(
        STRUCT(
            ip, country_code, os, channel_type
        )
        ORDER BY log_datetime DESC LIMIT 1
    )[OFFSET(0)].*,
FROM login_logs
GROUP BY user_id
;

示例 2:
用logkey过滤重复行,用key分组时相对→PARTITION BY

SELECT user_id, ip, country_code, os, channel_type
FROM login_logs
WHERE TRUE
QUALIFY ROW_NUMBER() OVER (PARTITION BY logkey ORDER BY log_datetime) = 1
;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-11
    相关资源
    最近更新 更多