Looker 中的对称汇总是一项非常强大的功能。不过,由于对称聚合看起来有点令人望而生畏,并且大多是在后台进行的,因此遇到它们时可能会感到有点困惑。本页面提供有关对称聚合的以下信息:
为什么需要对称汇总
SQL 是数据分析的语言,功能非常强大。但能力越强,责任也就越大,而分析人员有责任避免意外计算的汇总值,例如求和、平均值和计数。
错误地执行这些计算是出人意料的,这些类型的错误计算可能会给分析人员带来极大的困扰。以下示例说明了可能会出错的情况。
假设您有两个表:orders
和 order_items
。order_items
表会为订单中的每项记录一行,因此这两个表之间的关系是一对多关系。这是一种一对多关系,因为一个订单可以包含多个商品,但每个商品只能属于一个订单。如需获得有关如何确定联接的正确关系的指导,请参阅正确设置关系参数“最佳做法”页面。
orders
表如下所示:
order_id |
user_id |
total |
order_date |
---|---|---|---|
1 | 100 | 50.36 美元 | 2017-12-01 |
2 | 101 | 24.12 美元 | 2017-12-02 |
3 | 137 | 50.36 美元 | 2017-12-02 |
在这个 orders
表中,total
列中值 (SUM(total)
) 的值的总和等于 124.84
。
假设 order_items
表包含六行:
order_id |
item_id |
quantity |
unit_price |
---|---|---|---|
1 | 50 | 1 | 23.00 美元 |
1 | 63 | 2 | 13.68 美元 |
2 | 63 | 1 | 13.68 美元 |
2 | 72 | 1 | 5.08 美元 |
2 | 79 | 1 | 5.36 美元 |
3 | 78 | 1 | 50.36 美元 |
获取订购商品数量是很容易的。quantity
列中值的总和 (SUM(quantity)
) 是 7
。
现在,假设您使用 orders
表和 order_items
表的共享列 order_id
进行联接。这会生成以下表格:
order_id |
user_id |
total |
order_date |
item_id |
quantity |
unit_price |
---|---|---|---|---|---|---|
1 | 100 | 50.36 美元 | 2017-12-01 | 50 | 1 | 23.00 美元 |
1 | 100 | 50.36 美元 | 2017-12-01 | 63 | 2 | 13.68 美元 |
2 | 101 | 24.12 美元 | 2017-12-02 | 63 | 1 | 13.68 美元 |
2 | 101 | 24.12 美元 | 2017-12-02 | 72 | 1 | 5.08 美元 |
2 | 101 | 24.12 美元 | 2017-12-02 | 79 | 1 | 5.36 美元 |
3 | 137 | 50.36 美元 | 2017-12-02 | 78 | 1 | 50.36 美元 |
上表提供了新信息,例如 12 月 1 日订购了 2 件商品(order_date
列中的 2017-12-01
),12 月 2 日订购了 4 件商品(2017-12-02
)。之前的一些计算(例如 SUM(quantity)
计算)仍然有效。不过,如果您尝试计算总支出金额,则会遇到问题。
如果您使用之前的计算方式 SUM(total)
,则新表中 order_id
值为 1
的行对应的总值 50.36
将被统计两次,因为该订单包含两个不同的商品(item_id
值分别为 50
和 63
)。order_id
值为 2
的行对应的 24.12
总和将被统计三次,因为此订单包含三个不同的商品。因此,此表格的 SUM(total)
计算结果为 223.44
,而不是正确答案 124.84
。
虽然在使用两个小型示例表时很容易避免这种错误,但在现实生活中,由于拥有大量表和大量数据,解决这个问题会远远复杂得多。这就是人们在没有意识到的情况下可能会出现的错误计算。这就是对称聚合解决的问题。
对称汇总的运作方式
对称汇总可防止分析师和任何其他使用 Looker 的用户意外错误计算总和、平均值和计数等汇总值。对称汇总有助于减轻分析师的负担,因为分析师可以信任用户不会使用不正确的数据。对称汇总通过确保正确统计计算中的每个事实的次数,以及跟踪您正在计算的内容,来实现这一点。
在前面的示例中,对称汇总函数会识别出 total
是 orders
(而非 order_items
)的属性,因此它只需统计每个订单的总金额一次,即可得出正确答案。该函数通过使用分析师在 Looker 中定义的唯一主键来实现此目的。这意味着,当 Looker 对联接表进行计算时,它会发现,即使有两行 order_id
的值为 1
,也不应将总计数算两次,因为该总计数已包含在计算中,并且对于 order_id
的值为 2
的三行,也应只将总计数算一次。
值得注意的是,对称聚合依赖于唯一 主键以及模型中指定的正确联接关系。因此,如果您得到的结果有误,请与分析师联系,确保所有设置正确无误。
为什么对称汇总看起来很复杂
对称聚合的外观有点神秘。如果不使用对称汇总,Looker 通常会编写良好的 SQL,例如以下示例所示:
SELECT order_items.order_id AS "order_items.order_id", order_items.sale_price AS "order_items.sale_price" FROM order_items AS order_items GROUP BY 1,2 ORDER BY 1 LIMIT 500
使用对称聚合时,SQL Looker 写入内容可能如以下示例所示:
SELECT order_items.order_id AS "order_items.order_id", order_items.sale_price AS "order_items.sale_price", (COALESCE(CAST( ( SUM(DISTINCT (CAST(FLOOR(COALESCE(users.age ,0) *(1000000*1.0)) AS DECIMAL(38,0))) + CAST(STRTOL(LEFT(MD5(CONVERT(VARCHAR,users.id )),15),16) AS DECIMAL(38,0)) * 1.0e8 + CAST(STRTOL(RIGHT(MD5(CONVERT(VARCHAR,users.id )),15),16) AS DECIMAL(38,0)) ) - SUM(DISTINCT CAST(STRTOL(LEFT(MD5(CONVERT(VARCHAR,users.id )),15),16) AS DECIMAL(38,0)) * 1.0e8 + CAST(STRTOL(RIGHT(MD5(CONVERT(VARCHAR,users.id )),15),16) AS DECIMAL(38,0))) ) AS DOUBLE PRECISION) / CAST((1000000*1.0) AS DOUBLE PRECISION), 0) / NULLIF(COUNT(DISTINCT CASE WHEN users.age IS NOT NULL THEN users.id ELSE NULL END), 0)) AS "users.average_age FROM order_items AS order_items LEFT JOIN users AS users ON order_items.user_id = users.id GROUP BY 1,2 ORDER BY 3 DESC LIMIT 500
对称聚合采用的具体格式取决于 Looker 编写的 SQL 方言,但所有格式都执行相同的基本操作:如果多行具有相同的主键,则对称聚合函数只会将其统计一次。它通过使用 SQL 标准中鲜为人知的 SUM DISTINCT
和 AVG DISTINCT
函数来实现这一点。
如需了解具体原因,您可以使用对称汇总来完成之前的计算。在联接表中的七个列中,您只需要两个:要汇总的列 (total
) 和订单的唯一主键 (order_id
)。
order_id |
total |
---|---|
1 | 50.36 美元 |
1 | 50.36 美元 |
2 | 24.12 美元 |
2 | 24.12 美元 |
2 | 24.12 美元 |
3 | 50.26 美元 |
对称汇总会采用主键(在本例中为 order_id
),并为每个主键创建一个非常大的数字,该数字保证是唯一的,并且对于相同的输入,始终会给出相同的输出。(通常,它会使用哈希函数来实现此目的,但具体细节超出了本页的讨论范围。)结果应如下所示:
big_unique_number |
total |
---|---|
802959190063912 | 50.36 美元 |
802959190063912 | 50.36 美元 |
917651724816292 | 24.12 美元 |
917651724816292 | 24.12 美元 |
917651724816292 | 24.12 美元 |
110506994770727 | 50.36 美元 |
然后,对于每行,Looker 会执行以下操作:
SUM(DISTINCT big_unique_number + total) - SUM(DISTINCT big_unique_number)
这可靠地为您提供正确的汇总总计,对每个总计进行精确的计数次数。Looker 对称汇总函数不会被重复行或总金额相同的多个订单所迷惑。您可以尝试自行计算,以便更好地了解对称聚合的工作原理。
执行此操作所需的 SQL 并非最美观:使用 CAST()
、md5()
、SUM(DISTINCT)
和 STRTOL()
,您肯定不希望手动编写 SQL。不过,幸运的是,您无需这样做,Looker 可以为您编写 SQL。
如果某个汇总无需使用对称汇总即可正常运行,Looker 会自动检测到这一点,并不会使用该函数。由于对称汇总会带来一些性能开销,因此 Looker 能够区分何时使用对称汇总,何时不使用对称汇总,这进一步优化了 Looker 生成的 SQL,使其尽可能高效,同时仍能保证正确的答案。