测试给定ID是否存在于JSON列的任何行中

fykwrbwg  于 2023-10-21  发布在  其他
关注(0)|答案(2)|浏览(97)

在我的Postgres 13.10数据库中,我有一个表menus,它有一个名为dishes的JSON列。此列包含以下格式的值:

{
    "desserts": [
        {"id": 1, "name": "chocolate cake"},
        {"id": 2, "name": "banana split"}
    ],
    "appetizers": [
        {"id": 3, "name": "nachos"},
        {"id": 4, "name": "mozzarella sticks"},
        {"id": 5, "name": "buffalo wings"}
    ]
}

数据类型是json,但如果这有助于提高查询性能,我可以选择将其更改为jsonb
给定一个开胃菜ID列表(例如,3和5),我需要确定表中至少有一行引用了哪些ID。
我如何写一个查询来做到这一点?
(Of当然,这是一个人为的例子)。

d6kp6zgx

d6kp6zgx1#

假设**jsonb。(对于普通的json来说,无法很好地扩展)。
为了
快**,使用Postgres 12增加的SQL/JSON路径功能:

SELECT *
FROM   unnest ('{3,5}'::int[]) i(id)
WHERE  EXISTS (
   SELECT FROM menus
   WHERE  dishes @? format('$.*[*].id ? (@ == %s)', i.id)::jsonpath
   );

这将在menus.dishes的顶层对象中嵌套的所有数组中搜索键“id”。或者限制到顶级关键“开胃菜”:

SELECT *
FROM   unnest ('{3,5}'::int[]) i(id)
WHERE  EXISTS (
   SELECT FROM menus
   WHERE  dishes @? format('$.appetizers[*].id ? (@ == %s)', i.id)::jsonpath
   );

fiddle
确保menus(dishes)上有一个GIN索引。理想情况下,jsonb_path_ops索引:

CREATE INDEX menus_dishes ON menus USING gin (dishes jsonb_path_ops);

主要功能是动态构建一个jsonpath表达式。通过这种方式,它适用于任何数量的输入ID,JSON文档中任何数量的顶级对象,以及任何数量的嵌套数组项-同时仍然使用上述索引。

相关信息:

  • 过滤JSON值至少有一个键在给定数组中并具有给定值

靠边站
你有没有考虑过用你的JSON文档制作一个真正的关系DB?关键字"database normalization"。会使这样的查询更简单更快。

ttp71kqs

ttp71kqs2#

你没有描述你希望如何提供名单。在这里,我将其作为一个VALUES构造提供。
您可以使用@>包含测试,但需要将数据强制转换为JSONB(或者首先以这种方式存储数据)才能使用它。“至少一行”建议使用exists结构。

select t,
  exists (select 1 from menus where dishes::jsonb @> jsonb_build_object('appetizers',jsonb_build_array(jsonb_build_object('id',t))) )
from (values (3),(5),(9)) t(t);

 t | exists
---+--------
 3 | t
 5 | t
 9 | f

相关问题