【问题标题】:Using Postgres JSON Functions on table columns在表列上使用 Postgres JSON 函数
【发布时间】:2017-03-08 17:01:40
【问题描述】:

我进行了广泛的搜索(在 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 位

【问题讨论】:

    标签: sql json postgresql


    【解决方案1】:

    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'
    

    注意#>> operator 返回text 类型,#> 运算符返回jsonb 类型。正如jsonb_to_recordset 期望jsonb 作为它的第一个参数,我使用#>

    See it working at rextester.com

    【讨论】:

      【解决方案2】:

      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,所以不需要强制转换。

      【讨论】:

        猜你喜欢
        • 2021-07-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-30
        • 2015-08-13
        • 1970-01-01
        • 2017-05-20
        • 1970-01-01
        相关资源
        最近更新 更多