postgresjsonb选择json对象作为列

rjee0c15  于 2021-07-26  发布在  Java
关注(0)|答案(3)|浏览(324)

该表有一个id和step\u数据列。

id | step_data
--------------
a1 | {...}
a2 | {...}
a3 | {...}

其中step\u数据是嵌套结构,如下所示,其中元数据的实际键可以位于 events 物体。

{
  "events": [
    {
      "timestamp": "2021-04-07T17:46:13.739Z",
      "meta": [
        {
          "key": "length",
          "value": "0.898"
        },
        {
          "key": "height",
          "value": "607023104"
        },
        {
          "key": "weight",
          "value": "33509376"
        }
      ]
    },
    {
      "timestamp": "2021-04-07T17:46:13.781Z",
      "meta": [
        {
          "key": "color",
          "value": "0.007"
        },
        {
          "key": "count",
          "value": "641511424"
        },
        {
          "key": "age",
          "value": "0"
        }
      ]
    }
  ]
}

我可以提取一个像 length 很容易。

select cast(metadata ->> 'value' as double precision) as length,
       id
from (
         select jsonb_array_elements(jsonb_array_elements(step_data #> '{events}') #> '{meta}') metadata,
                id
         from table
     ) as parsed_keys
where metadata @> '{
  "key": "length"
}'::jsonb

怠速10.898a20.800
但是我真正需要的是从几个已知的键中提取元数据作为列,比如 length 以及 color . 我不知道如何在拆分数组后高效地获取另一列 jsonb_array_elements() .
有没有一种不用打电话就能做到这一点的有效方法 jsonb_array_elements() 再来一次,把每一个都连接起来?例如,结果集如下所示。
IDLENGTHCOLORWATA10.8980.00733509376a20.8001.00015812391
使用postgres 11.7。

0pizxfdo

0pizxfdo1#

对于postgres 11,我只能考虑取消两个级别的测试,然后聚合回一个键/值对,从中可以提取所需的键:

select t.id, 
       (x.att ->> 'length')::numeric as length,
       (x.att ->> 'color')::numeric as color,
       (x.att ->> 'weight')::numeric as weight
from the_table t
  cross join lateral (
     select jsonb_object_agg(m.item ->> 'key', m.item -> 'value') as att
     from jsonb_array_elements(t.step_data -> 'events') as e(event)
      cross join jsonb_array_elements(e.event -> 'meta') as m(item)
     where m.item ->> 'key' in ('color', 'length', 'weight')
  ) x
;

有了postgres 12,你可以写得简单一点:

select t.id, 
       jsonb_path_query_first(t.step_data, '$.events[*].meta[*] ? (@.key == "length").value') #>> '{}' as length,
       jsonb_path_query_first(t.step_data, '$.events[*].meta[*] ? (@.key == "color").value') #>> '{}' as color,
       jsonb_path_query_first(t.step_data, '$.events[*].meta[*] ? (@.key == "weight").value') #>> '{}' as weight
from the_table t
;
eqqqjvef

eqqqjvef2#

交叉表()

任何postgres版本。
你可以把结果输入 crosstab() 函数来透视结果。你需要额外的模块 tablefunc 安装。如果您不熟悉,请先阅读以下基本说明:
postgresql交叉表查询

SELECT *
FROM   crosstab(
   $$
   SELECT id, metadata->>'key', metadata ->>'value'
   FROM  (SELECT id, jsonb_array_elements(jsonb_array_elements(step_data -> 'events') -> 'meta') metadata FROM tbl) AS parsed_keys
   ORDER  BY 1
   $$
 , $$VALUES ('length'), ('color'), ('weight')$$
   ) AS ct (id text, length float, color float, weight float);

db<>在这里摆弄(全部演示)
应该提供最佳性能,特别是对于许多列。
请注意,我们不需要显式强制转换 double precision ( float ). crosstab() 过程 text 无论如何输入,结果将强制为列定义列表中给定的类型。
如果其中一个键出现多次,则最后一行获胜(您可以向$1中的查询添加确定性排序顺序,以便最后对首选行进行排序。示例:要获取每个键的最低值:

ORDER BY 1, 2, 3 DESC

带筛选子句的条件聚合

对于postgres 9.4或更新版本。
请参见:
使用附加(不同)筛选器聚合列

SELECT id
     , min(val) FILTER (WHERE key = 'length') AS length
     , min(val) FILTER (WHERE key = 'color') AS color
     , min(val) FILTER (WHERE key = 'weight') AS weight
FROM (
   SELECT id, metadata->>'key' AS key, (metadata ->>'value')::float AS val
   FROM  (SELECT id, jsonb_array_elements(jsonb_array_elements(step_data -> 'events') -> 'meta') metadata FROM tbl) AS parsed_keys
   ) sub
GROUP  BY id;

在12年级或更高的时候,我会把表现与 jsonb_path_query_first() . 一匹马提供了一个解决方案。

ljsrvy3e

ljsrvy3e3#

此查询是一个选项:

SELECT id,
  MAX(CASE WHEN metadata->>'key' = 'length' THEN  metadata->>'value' END) AS length,
  MAX(CASE WHEN metadata->>'key' = 'color' THEN  metadata->>'value' END) AS color,
  MAX(CASE WHEN metadata->>'key' = 'weight' THEN  metadata->>'value' END) AS weight

FROM (SELECT id, jsonb_array_elements(jsonb_array_elements(step_data #> '{events}') #> '{meta}') as metadata 
      FROM table t) AS aux

GROUP BY id;

相关问题