如何拆分列包括键和值到postgres中的单独列中

Posted

技术标签:

【中文标题】如何拆分列包括键和值到postgres中的单独列中【英文标题】:How to break apart a column includes keys and values into separate columns in postgres 【发布时间】:2020-03-29 13:38:10 【问题描述】:

我是 postgres 的新手,基本上没有经验。我有一个包含键和值的列的表。我需要编写一个查询,它返回一个表,其中包含表的所有列和附加列作为键作为列名和它下面的值。

我的输入是这样的:

id   | name|message
12478|  A  |img_type:=png,key_id:=f235, client_status:=active, request_status:=open
12598|  B  |img_type:=none,address_id:=c156, client_status:=active, request_status:=closed

输出将是:

id   |name| Img_type|Key_id|address_id|Client_status|Request_status
12478|  A | png     |f235  |NULL      |active       | open
12598|  B | none    |NULL  |c156      |active       | closed

任何帮助将不胜感激。

【问题讨论】:

你能改变message内容的格式吗?如果这是一个常规的 JSON 结构,那就容易多了 我已经提供了这些数据来阅读它并在其他程序中使用。我没有尝试更改格式。所以你的意思是在SELECT中基本上将消息列格式更改为json? 不,存储数据为有效的 JSON(即更改您的应用程序以更改格式) 【参考方案1】:

我唯一能想到的就是提取键/值对的正则表达式。

select id, name, 
       (regexp_match(message, '(img_type:=)([^,]+),0,1'))[2] as img_type,
       (regexp_match(message, '(key_id:=)([^,]+),0,1'))[2] as key_id,
       (regexp_match(message, '(client_status:=)([^,]+),0,1'))[2] as client_status,
       (regexp_match(message, '(request_status:=)([^,]+),0,1'))[2] as request_status
from the_table;

regexp_match 返回一个匹配数组。由于正则表达式包含两组(一组用于“键”,一组用于“值”),[2] 采用数组的第二个元素。


这非常昂贵且容易出错(例如,如果任何值包含 , 并且您需要处理引用的值)。如果您有机会更改存储值的应用程序,您应该认真考虑更改代码以存储正确的 JSON 值,例如

"img_type": "png", "key_id": "f235", "client_status": "active", "request_status": "open"'

然后你可以使用例如message ->> 'img_type' 检索键 img_type 的值


您可能还需要考虑一个正确规范化的表,其中每个键都是一个真实的列。

【讨论】:

【参考方案2】:

我可以用函数来做到这一点。 我对性能很肯定,但这是我的建议:

CREATE TYPE log_type AS (img_type TEXT, key_id TEXT, address_id TEXT, client_status TEXT, request_status TEXT);

CREATE OR REPLACE FUNCTION populate_log(data TEXT)
RETURNS log_type AS
$func$
DECLARE
    r log_type;
BEGIN
    select x.* into r
    from 
    (
        select 
            json_object(array_agg(array_data)) as json_data
        from (
            select unnest(string_to_array(trim(unnest(string_to_array(substring(populate_log.data, '[^]+'), ','))), ':=')) as array_data
        ) d
    ) d2,
    lateral json_to_record(json_data) as x(img_type text, key_id text, address_id text, client_status text, request_status text);
    RETURN r;
END
$func$  LANGUAGE plpgsql;


with log_data (id, name, message) as (
    values
    (12478, 'A', 'img_type:=png,key_id:=f235, client_status:=active, request_status:=open'),
    (12598, 'B', 'img_type:=none,address_id:=c156, client_status:=active, request_status:=closed')
)
select id, name, l.*
from log_data, lateral populate_log(message) as l;

你最终在查询中写的内容是这样的,假设数据在一个名为log_data的表中:

select id, name, l.*
from log_data, lateral populate_log(message) as l;

我认为message 列是一个文本,在 Postgres 中它可能是一个数组,在这种情况下你必须删除一些转换,string_to_array(substring(populate_log.data)) -> populate_log.data

【讨论】:

以上是关于如何拆分列包括键和值到postgres中的单独列中的主要内容,如果未能解决你的问题,请参考以下文章

如何交换散列中的键和值

如何将单独列中冒号前后的单词拆分为sql中的行

如何根据 pandas-python 中带有空格的图像拆分列中的值

如何将所有键和值从 Swift 中的 NSDictionary 获取到单独的字符串数组中?

如何使用正则表达式拆分列以将尾随 CAPS 移动到单独的列中?

如何从 Pyspark 中的 MapType 列获取键和值