0

我正在使用一个非常大的表(约 4.25 亿行,约 800 列,磁盘上约 750GB),看起来像(此处包括 4 列):

 id  | c1 |   c2   |   c3   | c4
-----+----+--------+--------+----
 101 | t  | green  | small  | 10
 102 | t  | green  | small  | 11
 103 | f  | red    | medium | 12
 104 | f  | yellow | medium | 13
 105 | t  | blue   | medium | 14
 106 | t  | green  | large  | 14

我想要做的是按每列中的每个唯一值将 id 分组在一起。输出如下所示:

colname | value  | ids
----------------------
c1      | t      | 101, 102, 105, 106
c1      | f      | 103, 104
c2      | green  | 101, 102, 106
c2      | red    | 103
c2      | yellow | 104
c2      | blue   | 105
c3      | small  | 101, 102
...

每列将有 2-15 个不同的值,因此每个分组中会有非常多的 id,因为总共有大约 4.25 亿个 id。我决定做的是将 id 分成特定大小的组,然后跟踪这些块,如下所示(仅显示 c1 列):

colname | value  | chunk | ids
------------------------------
c1      | t      | 1     | {101, 102, ...}
c1      | t      | 2     | {205, 206, ...}
c1      | t      | 3     | {331, 332, ...}
c1      | f      | 1     | {103, 104, ...}
...

我面临的问题是我不知道如何一次跨多个列检索这些结果。对于单个列,此 SQL 将提供上表:

select 'c1' as colname, tmp.c1 as value, tmp.chunk, array_agg(tmp.id) as ids
from (
  select id, c1, (row_number() over (partition by c1) - 1) / ARRAY_SIZE + 1 as chunk
  from my_large_table
) as tmp
group by (tmp.c2, tmp.chunk)

我担心的是必须运行此查询约 800 次(无论有多少列),每次读取约 4.25 亿行。我知道分组集提供了一种同时按多个不同集进行分组的方法,这意味着我们可以读取一次表格并获得我们正在寻找的所有结果,但我不知道如何在1) 合并分块行为和 2) 在结果集中保留相关的 colname 的方式,这对于我们正在考虑的所有分组集中的每个分组集都是必需的。

附加上下文:对于所需的最终结果集中的 id、列、值和块,排序并不重要。

任何帮助将不胜感激,谢谢。

4

1 回答 1

0

您可以使用横向连接在一个查询中表达这一点:

select colname, colval, 
       ceil(seqnum::numeric / array_size) as chunk,
       array_agg(id)
from (select v.colname, v.colval, t.id,
             row_number() over (partition by v.colname, v.colval) as seqnum
      from my_large_table t cross join lateral
           (values ('c1', c1::text),
                   ('c2', c2::text),
                   . . .
           ) v(colname, colval)
     ) c
group by colname, colval, chunk;

也就是说,与为每列运行一组单独的查询相比,我不确定这将如何执行。

于 2021-08-04T20:21:02.090 回答