【问题标题】:Turning arbitrarily many rows into columns in PostgreSQL在 PostgreSQL 中将任意多行转换为列
【发布时间】:2017-05-23 09:33:09
【问题描述】:

我在 Postgres 中有一个表,旨在以非结构化形式捕获信息并重建它。从该表中导出数据时,我需要重新应用一些结构并且正在苦苦挣扎。

目前,我有一个表格:

lbl |   name     |  value
----|------------|--------
1   | num        |       1
1   | colour     |   "Red"
1   | percentage |    25.0
2   | num        |       2
2   | colour     | "Green"
2   | percentage |    50.0
3   | num        |       3
3   | colour     |  "Blue"
3   | percentage |    75.0

而且我需要以这种形式生成一个表格:

lbl | num |  colour | percentage
----|-----|---------|------------
1   | 1   | "Red"   |   25.0
2   | 2   | "Green" |   50.0
3   | 3   | "Blue"  |   75.0

我已经建立了这个查询:

SELECT lbl, 
   max(case when name = 'num' then value else '-' end) num,
   max(case when name = 'colour' then value else '-' end) colour,
   max(case when name = 'percentage' then value else '-' end) percentage
FROM example_table
GROUP BY lbl

查询有效,但我需要将其扩展为包含任意数量的潜在名称值。我已经调查过 crossfunc 但无法让它按我的意图工作。任何帮助将不胜感激。

我在这里设置了一个 sqlfiddle 来帮助启动:http://sqlfiddle.com/#!9/8d3133/6/0

编辑:如果可以的话,我也可以使用 PL/pgSQL。

【问题讨论】:

  • 我觉得这里需要使用动态SQL。
  • 为名称包含任意数量的潜在值”,这在普通 SQL 中是不可能的。数据库运行查询之前必须知道查询的列数。
  • 啊,好的。如果我可以使用 plsql 可以吗?
  • 我认为那里有几个链接,我也许可以找到答案。他们中的大多数似乎都没有处理我有任意多个“名称”值的情况。对于一个已知的值,即使是一个很大的值,这似乎很容易,但就我而言,我不提前知道这些值是什么。

标签: sql postgresql pivot crosstab


【解决方案1】:

Postgres(和其他 RDBMS)中数据透视表的主要问题是查询结果的结构(列的数量和名称)不能因所选数据而异。一种可能的解决方案是动态创建视图,该视图的结构由数据定义。示例函数根据表example_table创建视图:

create or replace function create_pivot_view()
returns void language plpgsql as $$
declare
    list text;
begin
    select string_agg(format('jdata->>%1$L "%1$s"', name), ', ')
    from (
        select distinct name
        from example_table
        ) sub
    into list;

    execute format($f$
        drop view if exists example_pivot_view;
        create view example_pivot_view as
        select lbl, %s
        from (
            select lbl, json_object_agg(name, value) jdata
            from example_table
            group by 1
            order by 1
            ) sub
        $f$, list);
end $$;

修改表后(可能在触发器中)使用该函数,查询创建的视图:

select create_pivot_view();

select *
from example_pivot_view;

 lbl | num | colour | percentage 
-----+-----+--------+------------
   1 | 1   | Red    | 25.0
   2 | 2   | Green  | 50.0
   3 | 3   | Blue   | 75.0
(3 rows)

Test it in db<>fiddle.

请注意,只有在将新名称添加到表(或从中删除某些名称)后,才需要重新创建视图(调用函数)。如果一组不同的名称没有改变,您可以在不重新创建视图的情况下查询视图。如果该集合被频繁修改,创建一个临时视图将是一个更好的选择。

您可能也对Flatten aggregated key/value pairs from a JSONB field?感兴趣

【讨论】:

  • 并发呢?将create view example_pivot_view as 更改为declare example_pivot_view cursor for 并将select * from example_pivot_view; 更改为fetch all from example_pivot_view; - 会更安全。
  • 光标是个好主意。我不建议这样做,主要是因为正确使用游标的一般知识很少,解决方案对于普通读者来说已经够难了。
  • 没有简单的方法可以将从游标中获取的行与常规选择中的其他数据连接起来。
  • @George 从来没有关注过它,但可以create temporary view - IMO 这是最好的解决方案。
  • @Abelisto - 谢谢,我已将您的建议添加到答案中。
【解决方案2】:

试试这个

select
tab.ibl,
t1_num.value as "num",
t2_color.value as "colour",
t3_perc.value as "percentage"
from
(
    select distinct ibl from your_table order by tab.ibl desc
) tab
left join your_table t1_num on t1_num.ibl = tab.ibl and t1_num.name = 'num'
left join your_table t2_color on t2_color.ibl = tab.ibl and t2_color.name = 'colour'
left join your_table t3_perc on t3_perc.ibl = tab.ibl and t3_perc.name = 'percentage'

【讨论】:

  • 感谢您的回复,但这仍然需要提前知道名称列的潜在值。
猜你喜欢
  • 1970-01-01
  • 2015-09-11
  • 2012-12-14
  • 2019-10-15
  • 2020-08-07
  • 2018-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多