如何计算特定值在每列上出现的次数并按范围分组

How to count how many times a specific value appeared on each columns and group by range

我是 postgres 的新手,我有一个问题:

我有一个包含 100 列的 table。我需要计算每一列的值并计算它们出现的次数,这样我就可以根据它们适合的范围进行分组

我有一个 table 这样的(100 列)

+------+------+------+------+------+---------+--------+
| Name | PRB0 | PRB1 | PRB2 | PRB3 | ....... | PRB100 |
+------+------+------+------+------+---------+--------+
| A    |   15 |   6  |   47 |   54 |   ..... |      8 |
| B    |   25 |   22 |   84 |   86 |   ..... |     76 |
| C    |   57 |   57 |   96 |   38 |   ..... |     28 |
+------+------+------+------+------+---------+--------+

并且需要这样的输出

+------+---------------+----------------+----------------+----------------+-----+-----------------+--+
| Name | Count 0 to 20 | Count 21 to 40 | Count 41 to 60 | Count 61 to 70 | ... | Count 81 to 100 |  |
+------+---------------+----------------+----------------+----------------+-----+-----------------+--+
| A    |             5 |             46 |             87 |             34 | ... |              98 |  |
| B    |             5 |              2 |             34 |             56 | ... |              36 |  |
| C    |             7 |             17 |             56 |             78 | ... |              88 |  |
+------+---------------+----------------+----------------+----------------+-----+-----------------+--+

对于名称 A,我们有:

基本上,我需要像 Excel 上的函数 COUNTIFS 这样的东西。在 excel 我们只需要指定列的范围和条件。

您可以使用横向联接取消透视,然后聚合:

select
    name,
    count(*) filter(where prb between 0  and 20) cnt_00_20,
    count(*) filter(where prb between 21 and 50) cnt_21_20,
    ...,
    count(*) filter(where prb between 81 and 100) cnt_81_100
from mytable t
cross join lateral (values(t.prb0), (t.prb1), ..., (t.prb100)) p(prb)
group by name

但是请注意,这仍然需要您枚举 values() table 构造函数中的所有列。如果你想要完全动态的东西,你可以使用 json 代替。这个想法是使用 to_jsonb() 将每条记录转换为 json 对象,然后使用 jsonb_each() 转换为行;然后你可以进行条件聚合。

select 
    name,
    count(*) filter(where prb::int between 0  and 20) cnt_00_20,
    count(*) filter(where prb::int between 21 and 50) cnt_21_20,
    ...,
    count(*) filter(where prb::int between 81 and 100) cnt_81_100
from mytable t
cross join lateral to_jsonb(t) j(js)
cross join lateral jsonb_each( j.js - 'name') r(col, prb)
group by name