tim*_*hap 5 aggregate correlated-subquery amazon-redshift
我正在尝试找到在 Redshift 中表示和聚合高基数列的最佳方式。源是基于事件的,看起来像这样:
用户 | 时间戳 | 事件类型 |
---|---|---|
1 | 2021-01-01 12:00:00 | 富 |
1 | 2021-01-01 15:00:00 | 酒吧 |
2 | 2021-01-01 16:00:00 | 富 |
2 | 2021-01-01 19:00:00 | 富 |
在哪里:
我想将这些数据聚合成一个更小的数据集,每个用户只有一条记录(文档)。这些文件随后将被导出。感兴趣的聚合是这样的:
但是也:
我发现后一种情况很困难。
解决此问题的简单“列数据库友好”方法就是为每个事件类型创建一个聚合列:
用户 | nb_事件 | ... | NB_foo | nb_bar |
---|---|---|---|---|
1 | 2 | ... | 1 | 1 |
2 | 2 | ... | 2 | 0 |
但我认为这不是一个合适的解决方案,因为 event_type 字段是动态的,可能有数百或数千个值(Redshift 的上限为 1600 列)。而且,这个 event_type 字段上可能有多种类型的聚合(不仅仅是count
)。
第二种方法是将数据保持垂直形式,其中不是每个用户一行,而是每个(user, event_type)一行。然而,这实际上只是推迟了问题——在某些时候,数据仍然需要聚合成每个用户的单个记录以实现目标文档结构,并且列爆炸的问题仍然存在。
该数据的更自然的(我认为)表示是稀疏数组/文档/SUPER:
用户 | nb_事件 | ... | 按事件类型计数(超级) |
---|---|---|---|
1 | 2 | ... | {“foo”:1,“酒吧”:1} |
2 | 2 | ... | {“富”:2} |
这也几乎完全符合AWS 文档描述的预期超级用例:
当您需要存储相对较小的键值对集时,可以通过以 JSON 格式存储数据来节省空间。由于 JSON 字符串可以存储在单列中,因此使用 JSON 可能比以表格格式存储数据更有效。例如,假设您有一个稀疏表,其中需要有许多列来完全表示所有可能的属性,但对于任何给定行或任何给定列,大多数列值为 NULL。通过使用 JSON 进行存储,您可以将行数据存储在单个 JSON 字符串中的键:值对中,并消除稀疏填充的表列。
这就是我一直在尝试实施的方法。但我还没有完全实现我所希望的,主要是由于填充和聚合 SUPER 列的困难。这些描述如下:
如何从另一个 SELECT 查询插入这种 SUPER 列?所有 Redshift 文档仅在初始数据加载的上下文中真正讨论 SUPER 列(例如,通过使用json_parse
),但从未讨论从另一个 Redshift 查询生成此数据的情况。我理解这是因为首选方法是加载超级数据,但尽快将其转换为柱状数据。
如何重新聚合这种 SUPER 列,同时保留 SUPER 结构?到目前为止,我已经讨论了一个仅按用户聚合的简化示例。实际上,还有其他维度的聚合,对此表的某些分析将需要重新聚合上表中显示的值。通过类比,所需的输出可能类似于(聚合所有用户):
nb_事件 | ... | 按事件类型计数(超级) |
---|---|---|
4 | ... | {“foo”:3,“酒吧”:1} |
我可以通过如下查询接近实现这种重新聚合(其中listagg
键值字符串对是我不知道如何做的 SUPER 类型构造的替代):
select
sum(nb_events) nb_events,
(
select listagg(s)
from (
select
k::text || ':' || sum(v)::text as s
from my_aggregated_table inner_query,
unpivot inner_query.count_by_event_type as v at k
group by k
) a
) count_by_event_type
from my_aggregated_table outer_query
Run Code Online (Sandbox Code Playgroud)
但Redshift不支持这种关联查询:
[0A000] 错误:尚不支持这种类型的相关子查询模式
是否有任何替代方法可供考虑?通常我会使用 Spark 来处理此类问题,我发现 Spark 对于此类问题更加灵活。但如果可能的话,坚持使用 Redshift 会很棒,因为那是源数据所在的位置。