将单个 postgres jsonb 数组元素转换为行元素

Convert individual postgres jsonb array elements to row elements

我必须查询一个包含 2 列的 table,id 和 content。 Id 只是一个 uuid,内容栏看起来像

    {
  "fields": [
    {
      "001": "mig00004139229"
    },
    {
      "856": {
        "ind1": " ",
        "ind2": " ",
        "subfields": [
          {
            "u": "https://some.domain.com"
          },
          {
            "z": "some text"
          }
        ]
      }
    },
    {
      "999": {
        "subfields": [
          {
            "i": "81be1acf-11df-4d13-a5c6-4838e3a808ee"
          },
          {
            "s": "3a6aa357-8fd6-4451-aedc-13453c1f2296"
          }
        ]
      }
    }
  ]
}

我需要 select 子字段“u”域与字符串“domain.com”匹配的 id、001 和 856 元素,因此输出将是

id 001 856
81be1acf-11df-4d13-a5c6-4838e3a808ee mig00004139229 https://some.domain.com

如果这是一个平面 table,查询将对应于“select id, 001, 856 from table where 856 like '%domain.com%' “

我可以根据我需要的条件 select 单独的列,但它们出现在单独的行中,除了在常规 select 语句中与任何其他单独字段一起出现的 id。我怎样才能让其他字段出现在同一行中,因为它是同一记录的一部分?

不幸的是,我的 postgres 版本不支持 jsonb_path_query,所以我一直在尝试以下方法:

SELECT id, jsonb_array_elements(content -> 'fields') -> '001', 
     jsonb_array_elements(content -> 'fields') -> '856' -> 'subfields'
FROM 
     mytable
WHERE....

此方法 returns 我需要的数据,但各个元素到达单独的行,第一列中带有 id,每个元素都为 null,既不是 001 也不是 856,例如

id 001 856
id_for_first_record 001_first_record null
id_for_first_record null null
id_for_first_record null null
id_for_first_record null 856_first_record
id_for_second_record 001_second_record null
id_for_second_record null null
id_for_second_record null null
id_for_second_record null 856_second_record

可用,但笨重,所以我正在寻找更好的东西

我想我的查询可以帮到你。有多种方法可以解决这个问题,我不确定这是否是最好的方法。

我将 jsonb_path_query() 函数与指定 JSON 值的路径一起使用。

SELECT 
       id,  
       jsonb_path_query(content, '$.fields[*]."001"') AS "001",
       jsonb_path_query(content, '$.fields[*]."856".subfields[*].u') AS "856"
FROM t
WHERE jsonb_path_query_first(content, '$.fields[*]."856".subfields[*].u' )::text ilike '%domain%';

输出:

id 001 856
81be1acf-11df-4d13-a5c6-4838e3a808ee "mig00004139229" "https://some.domain.com"

更新: 因为 Postgresql 版本早于 12。

你可以试试这样的方法,但我认为一定有更好的方法:

SELECT 
       t.id,
       max(sq1."001") AS "001",
       max(sq2."856") AS "856"
       
FROM t
INNER JOIN (SELECT id, (jsonb_array_elements(content -> 'fields') -> '001')::text AS "001" FROM t) AS sq1 ON t.id = sq1.id
INNER JOIN (SELECT id, (jsonb_array_elements(jsonb_array_elements(content -> 'fields') -> '856' -> 'subfields') -> 'u')::text AS "856" FROM t) AS sq2 ON t.id = sq2.id

WHERE sq2."856" ilike '%domain%'

GROUP BY t.id;