在表列上使用Postgres JSON函数

use*_*122 7 sql postgresql json

我进行了广泛的搜索(在Postgres文档中以及在Google和SO上),以查找表中实际JSON列上使用的JSON函数的示例。

这是我的问题:我尝试使用来从列中的JSON对象数组中提取键值jsonb_to_recordset(),但会出现语法错误。当我将对象从字面上传递给函数时,它可以正常工作:

从字面上传递JSON:

select * 
from jsonb_to_recordset('[
    { "id": 0, "name": "400MB-PDF.pdf", "extension": ".pdf", 
        "transferId": "ap31fcoqcajjuqml6rng"}, 
    { "id": 0, "name": "1000MB-PDF.pdf", "extension": ".pdf", 
      "transferId": "ap31fcoqcajjuqml6rng"}
  ]') as f(name text);`
Run Code Online (Sandbox Code Playgroud)

结果是:

400MB-PDF.pdf
1000MB-PDF.pdf
Run Code Online (Sandbox Code Playgroud)

它提取键“名称”的值。

这是列中的JSON,使用以下方法提取:

select journal.data::jsonb#>>'{context,data,files}' 
from journal 
where id = 'ap32bbofopvo7pjgo07g';
Run Code Online (Sandbox Code Playgroud)

导致:

[ { "id": 0, "name": "400MB-PDF.pdf", "extension": ".pdf", 
    "transferId": "ap31fcoqcajjuqml6rng"}, 
  { "id": 0, "name": "1000MB-PDF.pdf", "extension": ".pdf", 
    "transferId": "ap31fcoqcajjuqml6rng"}
]
Run Code Online (Sandbox Code Playgroud)

但是当我尝试将jsonb#>>'{context,data,files}'传递给jsonb_to_recordset()时,如下所示:

select id, 
       journal.data::jsonb#>>::jsonb_to_recordset('{context,data,files}') as f(name text) 
from journal 
where id = 'ap32bbofopvo7pjgo07g';
Run Code Online (Sandbox Code Playgroud)

我收到语法错误。我尝试了不同的方法,但是每次它抱怨语法错误时:

版本:x86_64-unknown-linux-gnu上的PostgreSQL 9.4.10,由gcc(Ubuntu 4.8.2-19ubuntu1)4.8.2,64位编译

dmf*_*fay 8

jsonb_to_recordset是一个定值函数,只能在特定的地方调用。该FROM子句就是这样的一个地方,这就是为什么您的第一个示例有效,但该SELECT子句无效的原因。

为了将您的 JSON 数组变成您可以查询的“表”,您需要使用横向连接。效果很像源记录集上的 foreach 循环,这就是您应用该jsonb_to_recordset函数的地方。这是一个示例数据集:

create table jstuff (id int, val jsonb);

insert into jstuff
values
    (1, '[{"outer": {"inner": "a"}}, {"outer": {"inner": "b"}}]'),
    (2, '[{"outer": {"inner": "c"}}]');
Run Code Online (Sandbox Code Playgroud)

一个简单的横向连接查询:

select id, r.*
from jstuff
join lateral jsonb_to_recordset(val) as r("outer" jsonb) on true;

 id |     outer      
----+----------------
  1 | {"inner": "a"}
  1 | {"inner": "b"}
  2 | {"inner": "c"}
(3 rows)
Run Code Online (Sandbox Code Playgroud)

这是最难的部分。请注意,您必须在AS子句中定义新记录集的外观——因为我们val数组中的每个元素都是一个 JSON 对象,带有一个名为“outer”的字段,这就是我们给它的。如果您的数组元素包含您感兴趣的多个字段,您可以以类似的方式声明这些字段。另请注意,您的 JSON 架构需要保持一致:如果数组元素不包含名为“outer”的键,则结果值为 null。

从这里开始,您只需要像以前一样使用遍历运算符从每个 JSON 对象中提取您需要的特定值。如果我只想要样本数据集中的“内部”值,我会指定select id, r.outer->>'inner'. 由于它已经是 JSONB,因此不需要强制转换。


And*_*mar 6

之后的表达式select必须求值为单个值。由于jsonb_to_recordset返回的是一组行和列,因此您不能在那里使用它。

解决方案是cross join lateral,它允许您使用函数将一行扩展为多行。这样select就可以执行单行操作。例如:

select  *
from    journal j
cross join lateral
        jsonb_to_recordset(j.data#>'{context, data, files}') as d(id int, name text)
where   j.id = 'ap32bbofopvo7pjgo07g'
Run Code Online (Sandbox Code Playgroud)

请注意,#>>运算符返回type text,而#>运算符返回type jsonb。如jsonb_to_recordset预期的jsonb那样,我正在使用#>

在rextester.com上查看它的运行情况