将未知行值动态转置为 postgres 上的列名

Dynamic transpose for unknown row value into column name on postgres

我有table这样的:

customer_number label value
1 address St. John 1A
1 phone 111111111
1 email john@cena.com
2 address St. Marry 231A
2 phone 222222222
2 email please@marry.me

我想要新的 table 或视图,所以它变成了:

customer_number address phone email
1 St. John 1A 111111111 john@cena.com
2 St. Marry 231A 222222222 please@marry.me

但将来可能会添加不同的标签,例如可能会有名为 occupation 的新标签。

重要提示,我不知道标签列的值,所以它应该迭代到该列内的任何值。

有什么办法吗?

我使用交叉应用来解决这个问题.. 这是我的查询

select distinct tb9.customer_number, tb9_2.*
from Table_9 tb9 cross apply
     (select max(case when tb9_2.[label] like '%address%' then [value] end) as [address],
             max(case when tb9_2.[label] like '%phone%' then [value] end) as [phone],
             max(case when tb9_2.[label] like '%email%' then [value] end) as [email]
      from Table_9 tb9_2
      where tb9.customer_number = tb9_2.customer_number
     ) tb9_2;

一般来说SQL不擅长动态旋转

这是一个将为您转换数据的查询。但是,它不是动态的,即如果添加了未来的 occupation 标签,那么您将不得不更改查询。不确定是否可以接受:

select customer_number,
max(value) filter (where label='address') as address,
max(value) filter (where label='phone') as phone,
max(value) filter (where label='email') as email
from your_customer_table
group by customer_number

假设您是 运行 Postgres 9.4 或更高版本,以便支持 filter 功能。如果不是,则可以使用 case 语句重新处理它:

select customer_number,
max(case when label='address' then value else null end) as address,
max(case when label='phone' then value else null end) as phone,
max(case when label='email' then value else null end) as email
from your_customer_table
group by customer_number

您不能拥有“动态”数据透视表,因为查询的所有列的编号、名称和数据类型必须在查询实际执行之前为数据库所知执行(即在解析时)。

我发现将东西聚合成一个 JSON 更容易处理。

select customer_number,
       jsonb_object_agg(label, value) as props
from the_table
group by customer_number

如果您的前端可以直接处理 JSON 值,您可以到此为止。

如果您真的需要一个每个属性一列的视图,您可以从 JSON 值:

select customer_number, 
       props ->> 'address' as address,
       props ->> 'phone' as phone,
       props ->> 'email' as email
from (       
  select customer_number,
         jsonb_object_agg(label, value) as props
  from the_table
  group by customer_number
) t

我发现添加新属性后这更容易管理。


如果您需要一个包含所有标签的视图,您可以创建一个存储过程来动态创建它。如果不同标签的数量不会经常变化,这可能是一个解决方案:

create procedure create_customer_view() 
as
$$
declare
  l_sql text;
  l_columns text;
begin
  select string_agg(distinct format('(props ->> %L) as %I', label, label), ', ')
    into l_columns
  from the_table;
  
  l_sql := 
    'create view customer_properties as 
     select customer_number, '||l_columns||' 
     from (
      select customer_number, jsonb_object_agg(label, value) as props
       from the_table 
       group by customer_number 
     ) t';
  execute l_sql;
end;
$$
language plpgsql;

然后使用以下方法创建视图:

call create_customer_view();  

并且在您的代码中只需使用:

select *
from customer_properties;

您可以定期将该过程安排到 运行(例如,通过 Linux 上的 cron 作业)