在表列上使用Postgres JSON函数

时间:2017-03-08 17:01:40

标签: sql json postgresql

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

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

按字面意思传递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);`

结果:

400MB-PDF.pdf
1000MB-PDF.pdf

它提取键“name”的值。

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

select journal.data::jsonb#>>'{context,data,files}' 
from journal 
where id = 'ap32bbofopvo7pjgo07g';

导致:

[ { "id": 0, "name": "400MB-PDF.pdf", "extension": ".pdf", 
    "transferId": "ap31fcoqcajjuqml6rng"}, 
  { "id": 0, "name": "1000MB-PDF.pdf", "extension": ".pdf", 
    "transferId": "ap31fcoqcajjuqml6rng"}
]

但是当我尝试将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';

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

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

2 个答案:

答案 0 :(得分:5)

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'

请注意,See it working at rextester.com会返回text类型,而#>运算符会返回类型jsonb。由于jsonb_to_recordset期望jsonb作为其第一个参数我使用#>

{{3}}

答案 1 :(得分:4)

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"}}]');

简单的横向连接查询:

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)

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

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