【问题标题】:Efficient GROUP BY a CASE expression in Amazon Redshift/PostgreSQLAmazon Redshift/PostgreSQL 中的高效 GROUP BY 表达式
【发布时间】:2014-01-16 03:17:21
【问题描述】:

在分析处理中,通常需要将“不重要”的数据组折叠到结果表中的单行中。一种方法是通过 CASE 表达式对不重要的组进行 GROUP BY,其中不重要的组通过返回单个值的 CASE 表达式合并为一行,例如,组为 NULL。这个问题是关于在 Amazon Redshift 中执行此分组的有效方法,它基于 ParAccel:在功能方面接近 PosgreSQL 8.0。

例如,考虑在表中的 typeurl 上的 GROUP BY,其中每一行都是单个 URL 访问。目标是执行聚合,以便为 URL 访问计数超过某个阈值的每个 (type, url) 对发出一行,并为 all (type , url) 对,其中访问计数低于该阈值。结果表中的其余列将具有基于此分组的 SUM/COUNT 聚合。

比如下面的数据

+------+----------------------+-----------------------+
| type | url                  | < 50+ other columns > |
+------+----------------------+-----------------------+
|  A   | http://popular.com   |                       |
|  A   | http://popular.com   |                       |
|  A   | < 9997 more times>   |                       |
|  A   | http://popular.com   |                       |
|  A   | http://small-one.com |                       |
|  B   | http://tiny.com      |                       |
|  B   | http://tiny-too.com  |                       |

应该产生如下阈值为10000的结果表

+------+------------------------------------+--------------------------+
| type | url                  | visit_count | < SUM/COUNT aggregates > |
+------+------------------------------------+--------------------------+
|  A   | http://popular.com   |       10000 |                          |
|  A   |                      |           1 |                          |
|  B   |                      |           2 |                          |

总结:

Amazon Redshift 有一定的子查询关联限制,需要小心谨慎。下面 Gordon Linoff 的回答(已接受的答案)展示了如何使用双重聚合执行 GROUP BY a CASE 表达式,并在结果列和外部 GROUP BY 子句中复制表达式。

with temp_counts as (SELECT type, url, COUNT(*) as cnt FROM t GROUP BY type, url)
select type, (case when cnt >= 10000 then url end) as url, sum(cnt) as cnt
from temp_counts
group by type, (case when cnt >= 10000 then url end)

进一步的测试表明,双重聚合可以“展开”成一个 UNION ALL 独立查询,涉及每个独立的 CASE 表达式。在这个具有大约 200M 行的样本数据集的特殊情况下,这种方法的执行速度始终快了大约 30%。但是,该结果是特定于模式和数据的。

with temp_counts as (SELECT type, url, COUNT(*) as cnt FROM t GROUP BY type, url)
select * from temp_counts WHERE cnt >= 10000
UNION ALL
SELECT type, NULL as url, SUM(cnt) as cnt from temp_counts 
WHERE cnt < 10000 
GROUP BY type

这表明了在 Amazon Redshift 中实施和优化任意脱节分组和汇总的两种通用模式。如果性能对您很重要,请对两者进行基准测试。

【问题讨论】:

    标签: sql postgresql group-by amazon-redshift paraccel


    【解决方案1】:

    你可以用两个聚合来做到这一点:

    select type, (case when cnt > XXX then url end) as url, sum(cnt) as visit_cnt
    from (select type, url, count(*) as cnt
          from t
          group by type, url
         ) t
    group by type, (case when cnt > XXX then url end)
    order by type, sum(cnt) desc;
    

    【讨论】:

    • 我一开始也玩过这个路径,但它产生以下错误:执行SQL命令时发生错误:选择类型,(当cnt> = 10000然后url结束时)作为url, sum(cnt) as visit_cnt from (select type, url, ... 错误:列“t.cnt”必须出现在 GROUP BY 子句中或用于聚合函数 [SQL State=42803]
    • 我追踪到这个特定功能的错误——在 GROUP BY 列中引用了相关子查询的结果——Amazon Redshift 不支持,并用信息更新了问题。跨度>
    • @Sim 。 . .试试group by中表达式的版本(我刚刚修改过)。
    • 嗯,如果我将嵌套的 SELECT 外部化为 WITH 子句,上面的示例(使用双重聚合)始终比两个 SELECT 的 UNION 慢约 30%:一个 WHERE cnt > XXX 另一个形式为 SELECT 类型,NULL 作为 url,SUM(cnt) from temp_counts WHERE cnt
    • @Sim 。 . . Postgres 实现了 CTE。所以聚合只运行一次。这可能会显着加快union 查询的速度。我建议使用union all 而不是union 更快。
    【解决方案2】:
    1. 首先,你在type, url上分组。
    2. 然后你第二次在type, case when visit_count &lt; 10000 then NULL else url上分组。

    我使用过 SQL Server 语法,我希望它也适用于 Postgres。

    【讨论】:

    • 唉,这种方法会产生 SQL 错误。请参阅我对戈登上面的回答的cmets。看来我们需要一种不同的方法。
    • 这是一个奇怪的错误,因为 Gordon 的查询根本不使用子查询。它使用派生表。没有必要去相关。你在运行之前修改了他的查询吗?也许你添加了一个子查询。
    • Gordon 更新后的查询有效。感谢您的回答和跟进。
    猜你喜欢
    • 2021-04-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-11
    • 2011-08-14
    • 2013-10-17
    • 2017-09-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多