【问题标题】:Extract all the values in jsonb into a row将jsonb中的所有值提取到一行中
【发布时间】:2021-07-05 17:50:23
【问题描述】:

我正在使用 postgresql 11,我有一个 jsonb 代表该表的一行,它看起来像

{"userid":"test","rolename":"Root","loginerror":0,"email":"superadmin@ae.com",...,"thirdpartyauthenticationkey":{}}

有没有什么方法可以将 jsonb 的所有“值”收集到一个由 ',' 分隔且没有键的字符串中?

我想用上面的jsonb得到的字符串是这样的

(test, Root, 0, superadmin@ae.com, ..., {})

我需要将这些值的顺序保留为它们在 jsonb 中的键。我可以用 postgresql 做到这一点吗?

【问题讨论】:

  • 请注意,JSONB 类型不保留键顺序,因此,实际上,您可以使用类似 (superadmin@ae.com, test, Root, 0, ..., {}}) 的东西来代替。
  • (根据您的最新编辑):我认为jsonb_array_elements(jsonb_path_query_array(js, '$.keyvalue()')) WITH ORDINALITY 可能会有所帮助,但它没有keep the order either。此时,您最好的选择可能是 plv8 函数。

标签: postgresql jsonb


【解决方案1】:

您可以使用jsonb_populate_record 函数(假设您的json 数据与users 表匹配)。这将强制文本值与用户表的顺序相匹配: 架构 (PostgreSQL v13)

CREATE TABLE users (
  userid text,
  rolename text,
  loginerror int,
  email text,
  thirdpartyauthenticationkey json
)
  

查询 #1

WITH d(js) AS (
  VALUES
      ('{"userid":"test", "rolename":"Root", "loginerror":0, "email":"superadmin@ae.com", "thirdpartyauthenticationkey":{}}'::jsonb),
      ('{"userid":"other", "rolename":"User", "loginerror":324, "email":"nope@ae.com", "thirdpartyauthenticationkey":{}}'::jsonb)
)
SELECT jsonb_populate_record(null::users, js),
       jsonb_populate_record(null::users, js)::text AS record_as_text,
       pg_typeof(jsonb_populate_record(null::users, js)::text)
FROM d
;
jsonb_populate_record record_as_text pg_typeof
(test,Root,0,superadmin@ae.com,{}) (test,Root,0,superadmin@ae.com,{}) text
(other,User,324,nope@ae.com,{}) (other,User,324,nope@ae.com,{}) text

请注意,如果您正在构建此字符串以将其插入回 postgresql,那么您不需要这样做,因为jsonb_populate_record 的结果将匹配您的表:

查询 #2

WITH d(js) AS (
  VALUES
      ('{"userid":"test", "rolename":"Root", "loginerror":0, "email":"superadmin@ae.com", "thirdpartyauthenticationkey":{}}'::jsonb),
      ('{"userid":"other", "rolename":"User", "loginerror":324, "email":"nope@ae.com", "thirdpartyauthenticationkey":{}}'::jsonb)
)
INSERT INTO users
SELECT (jsonb_populate_record(null::users, js)).*
FROM d;

没有要显示的结果。


查询 #3

SELECT * FROM users;
userid rolename loginerror email thirdpartyauthenticationkey
test Root 0 superadmin@ae.com [object Object]
other User 324 nope@ae.com [object Object]

View on DB Fiddle

【讨论】:

  • 我不会将字符串插回我的表中。实际上,我会将聚合后的字符串插入到另一个表的 varchar 列中,因此它与记录来自的原始表无关。
【解决方案2】:

您可以使用jsonb_each_text() 获取一组元素的文本表示,string_agg() 将它们聚合为逗号分隔的字符串,concat() 将其放在括号中。

SELECT concat('(', string_agg(value, ', '), ')')
       FROM jsonb_each_text('{"userid":"test","rolename":"Root","loginerror":0,"email":"superadmin@ae.com","thirdpartyauthenticationkey":{}}'::jsonb) jet (key,
                                                                                                                                                           value);

db<>fiddle

您没有提供 JSON 可能驻留在的(该)表的 DDL 和 DML(如果提供,您的问题并不清楚)。因此,上面的演示仅使用您作为标量显示的 JSON。如果你确实有一张桌子,你需要CROSS JOIN LATERALGROUP BY 一些键。


编辑:

如果您需要确保保留订单并且您没有像@Marth 的回答所假设的那样在表结构中定义该订单,那么您当然可以按照您需要的顺序手动提取每个值。

SELECT concat('(',
              concat_ws(', ',
                       j->>'userid',
                       j->>'rolename',
                       j->>'loginerror',
                       j->>'email',
                       j->>'thirdpartyauthenticationkey'),
              ')')
       FROM (VALUES ('{"userid":"test","rolename":"Root","loginerror":0,"email":"superadmin@ae.com","thirdpartyauthenticationkey":{}}'::jsonb)) v (j);

db<>fiddle

【讨论】:

  • 我刚刚意识到我们的两种解决方案都没有保留键顺序(这并不是问题中的真正要求,但是以未指定的顺序生成带有键的字符串可能还不是全部有用:))。
  • 是的,我尝试了两种解决方案,发现这两种解决方案都没有保持键顺序,但我有必要保持原样,因为结果字符串没有t 包含任何键,所以我只能通过它们的 ORDER 来判断值。可以修改方法以保持顺序吗?
  • @Alanight:查看编辑。或者使用数学答案提供的巧妙技巧。
  • 顺便说一句concat('(', concat_ws(', ', j-&gt;&gt;'userid', j-&gt;&gt;'rolename', j-&gt;&gt;'loginerror', ...), ')')
  • @Abelisto:哦,是的,这绝对是一些改进。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-20
  • 2016-03-09
  • 1970-01-01
  • 2020-01-02
  • 1970-01-01
  • 1970-01-01
  • 2018-03-08
相关资源
最近更新 更多