如何过滤 json 列中嵌套值的行?

How to filter rows on nested values in a json column?

这是我的table(经过简化,只有重要的列):

CREATE TABLE things (
  id serial primary key
, name varchar
, blueprint json default '{}'
);

和一些示例数据:

# select * from things;

 id |  name   |                                  blueprint
----+---------+-----------------------------------------------------------------------------
  1 | Thing 1 | {}
  2 | Thing 2 | {"1":{"name":"Iskapola","wight":"2"}}
  3 | Thing 3 | {"1":{"name":"Azamund","weight":"3"}, "2":{"name":"Iskapola","weight":"1"}}
  4 | Thing 4 | {"1":{"name":"Ulamir","weight":"1"}, "2":{"name":"Azamund","weight":"1"}}

我想要 select 在 name 键下的任意位置有 'Azamund' 的行。 像这样:

# select * from things where * ->> 'name' = 'Azamund';

 id |      blueprint
----+----------------------------------------------------------------------------
  7 | {"1":{"name":"Azamund","weight":"3"}, "2":{"name":"Iskapola","weight":"1"}}
  8 | {"1":{"name":"Ulamir","weight":"1"}, "2":{"name":"Azamund","weight":"1"}}

数据的嵌套与示例中的完全相同(只有一层)。
目前我们使用的是 PostgreSQL 9.3.5.

在 PostgreSQL 9.3 中可以吗?也许是 9.4?

我可以执行的最接近的查询(我需要的 returns 数据)是:

select *
from (select id, (json_each(blueprint)).value::json->>'name' as name
      from stocks) as t
where t.name ~* 'azamund';

嗯...也许有更好的东西?

您的查询已结束。 json_each() 是关键函数。或者 jsonb_each() 对应 jsonb。一些改进:

SELECT *
FROM   things t
WHERE  EXISTS (
   SELECT FROM json_each(t.blueprint) b
   WHERE  b.value->>'name' ILIKE 'azamund'
   );

sqlfiddle
db<>fiddle here

  • json_each() 已经 returns 值作为 json 数据类型。不需要额外的转换。

  • 更好的是,在 EXISTS 中使用 LATERAL 引用。这比在 SELECT 列表中使用 set-returning 函数取消嵌套要干净得多。相关:

    • Call a set-returning function with an array argument multiple times
  • 使用 ILIKE (~~*) 进行模式匹配。正则表达式匹配 (~, ~*) 更强大,但也更昂贵。所以尽可能使用基本的 LIKE / ILKE 。详情:

替代 JSON 数组

你已经看过我对 JSON 数组的相关回答:

  • How do I query using fields inside the new PostgreSQL JSON datatype?

虽然嵌套 JSON 对象的查询看起来很简单,但数组有更好的 索引支持

  • Index for finding an element in a JSON array

在 Postgres 12 中使用 SQL/JSON 可能会变得更简单/更高效 ...