将未知行值动态转置为 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
作业)
我有table这样的:
customer_number | label | value |
---|---|---|
1 | address | St. John 1A |
1 | phone | 111111111 |
1 | john@cena.com | |
2 | address | St. Marry 231A |
2 | phone | 222222222 |
2 | please@marry.me |
我想要新的 table 或视图,所以它变成了:
customer_number | address | phone | |
---|---|---|---|
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
作业)