PostgreSQL:根据 JSON 数组中 JSON 的值对行进行排序

PostgreSQL: Sorting the rows based on value of a JSON in an array of JSON

A table 说 products 有一个名为 identifiers 的 JSONB 列,它存储 JSON 个对象的数组。

产品中的示例数据

 id  |     name    |      identifiers
-----|-------------|---------------------------------------------------------------------------------------------------------------
  1  | umbrella    | [{"id": "productID-umbrella-123", "domain": "ecommerce.com"}, {"id": "amzn-123", "domain": "amzn.com"}]
  2  | ball        | [{"id": "amzn-234", "domain": "amzn.com"}]
  3  | bat         | [{"id": "productID-bat-234", "domain": "ecommerce.com"}]

现在,我必须编写一个查询,根据域“amzn.com”

的“id”值对 table 中的元素进行排序

预期结果

 id   |     name     |      identifiers
----- |--------------|---------------------------------------------------------------------------------------------------------------
  3   | bat          |  [{"id": "productID-bat-234", "domain": "ecommerce.com"}]
  1   | umbrella     |  [{"id": "productID-umbrella-123", "domain": "ecommerce.com"}, {"id": "amzn-123", "domain": "amzn.com"}]
  2   | ball         |  [{"id": "amzn-234", "domain": "amzn.com"}]

amzn.com 的 ID 是“amzn-123”和“amzn-234”。 当按 amzn.com 的 id 排序时,首先出现“amzn-123”,然后是“amzn-234”

按域“amzn.com”的“id”值对 table 进行排序, id 为 3 的记录首先出现,因为 amzn.com 的 id 为 NULL, 后跟 ID 为 1 和 2 的记录,该记录具有已排序的有效 ID。

我真的不知道如何为这个用例编写查询。 如果它是 JSONB 而不是 JSON 的数组,我会尝试的。

是否可以在 PostgreSQL 中为此类用例编写查询? 如果是,请至少给我一个伪代码或粗略的查询。

由于您不知道数组中的位置,因此您需要遍历所有数组元素以找到亚马逊 ID。

获得 ID 后,您可以将其与 order by 一起使用。使用 nulls first 将那些没有亚马逊 ID 的产品放在顶部。

select p.*, a.amazon_id
from products p
   left join lateral (
      select item ->> 'id' as amazon_id
      from jsonb_array_elements(p.identifiers) as x(item)
      where x.item ->> 'domain' = 'amzn.com'
      limit 1 --<< safe guard in case there is more than one amazon id
   ) a on true --<< we don't really need a join condition
order by a.amazon_id nulls first;

Online example


对于 Postgres 12,这会更短一些:

select p.*
from products p
order by jsonb_path_query_first(identifiers, '$[*] ? (@.domain == "amzn.com").id') nulls first

经过一些调整,这是最终成功的查询,

select p.*, amzn -> 'id' AS amzn_id
from products p left join lateral JSONB_ARRAY_ELEMENTS(p.identifiers) amzn ON amzn->>'domain' = 'amzn.com' 
order by amzn_id nulls first