如何在postgresql中将两行转换为键值json对象?

时间:2015-08-14 14:32:58

标签: sql json postgresql key-value

我有一个像下面这样的数据模型,它被简化为只显示这个问题( SQL Fiddle 链接在底部):

enter image description here

人在数据库中表示为具有名称和多个属性的元表行,这些属性作为键值对存储在数据表中(键和值在不同的列中)。

预期结果

现在我想检索具有所有属性的所有用户。属性应作为json对象返回到单独的列中。例如:

name, data
Florian, { "age":23, "color":"blue" }
Markus, { "age":24, "color":"green" }

我的方法

现在我的问题是,我找不到在postgres中创建键值对的方法。我试过以下:

SELECT
  name,
  array_to_json(array_agg(row(d.key, d.value))) AS data
FROM meta AS m
JOIN (
  SELECT d.fk_id, d.key, d.value AS value FROM data AS d
  ) AS d
ON d.fk_id = m.id
GROUP BY m.name;

但它将此作为数据列返回:

[{"f1":"age","f2":24},{"f1":"color","f2":"blue"}]

其他解决方案

我知道有一个函数crosstab,它使我能够将数据表转换为一个键作为列,将值作为行表。但这不是动态的。我不知道一个人在数据表中有多少属性。所以这不是一个选择。

我还可以使用两个行值创建一个类似json的字符串并聚合它们。但也许有一个更好的解决方案。

不,不可能改变数据模型,因为真实数据模型已经在多方使用。

SQLFiddle

检查并测试我为此问题创建的小提琴: http://sqlfiddle.com/#!15/bd579/14

2 个答案:

答案 0 :(得分:6)

试试这个:

select 
    name, 
    json_object(array_agg(key), array_agg(replace(value::text, '"', ''))) as data
from data
join meta on fk_id = id
group by 1

Postgres 9.4中引入了json_object(keys text[], values text[])功能。

答案 1 :(得分:0)

当我需要更新一些JSON并删除数据库中的一些元素时,我遇到了同样的问题。下面的查询对我来说效果很好,因为它保留了字符串引号,但没有将它们添加到数字中。

select
  '{' || substr(x.arr, 3, length(x.arr) - 4) || '}'
from
(
  select
     replace(replace(cast(array_agg(xx) as varchar), '\"', '"'), '","', ', ') as arr
  from
  (
  select
    elem.key,
    elem.value,
    '"' || elem.key || '":' || elem.value as xx
  from
    quote q
      cross join
    json_each(q.detail::json -> 'bQuoteDetail'-> 'quoteHC'->0)  as elem  
  where
    elem.key != 'allRiskItems'  
  ) f 
) x