存在于 JSON 字段检查查询

Existance in JSON field check query

我在我的数据库中使用 PostgreSQL JSON 类型字段。

JSON格式:

[
   {"id": "13", "something": "not_important"},
   {"id": "5426565165191919", "something": "not_important"},
   {"id": "46", "something": "not_important"}
   ...
]

ID 列表,例如 ["13", "46", "84"]

如何进行查询,告诉我某些 ID 不存在? (哪一个不重要。)

WITH t(data) AS (
   VALUES ('           [
   {"id": "13", "something": "not_important"},
   {"id": "5426565165191919", "something": "not_important"},
   {"id": "46", "something": "not_important"}
   ]'::json)
   )
SELECT NOT EXISTS (
   SELECT data::text, o::text  -- cast to text for display in fiddle
   FROM   t, json_array_elements(t.data) AS d(o)
   RIGHT  JOIN unnest('{13,46,84}'::bigint[]) u(id) ON u.id = (d.o->>'id')::bigint
   WHERE  d.o IS NULL
   ) AS all_found;
  • CTE(WITH 查询)只是替代实际的 table t
  • 使用LEFT JOIN / NOT NULL 识别缺失值。细节:
    • Select rows which are not present in other table
  • 假设 bigint 足够宽以容纳您的 id 值。否则,切换到 numeric.

SQL Fiddle.

此相关答案中的更多解释:

  • Query for array elements inside JSON type

您可以通过将 "expected" ID 与 "actual" ID 左连接,然后在结果集中过滤 "actual" ID [=59] 的行来完成此操作=] 加入失败。唯一的技巧是从 JSON 中提取整数 id 值。假设您在 SQL:

中对 all id 进行硬编码,这就是它可以完成的方式
select
    expected.id
from
    (select substring(value::varchar from 2 for char_length(value::varchar)-2)::bigint id from json_array_elements('["13","46","84"]'::json)) expected
    left join (select (value->>'id')::bigint id from json_array_elements('[{"id":"13"},{"id":"5426565165191919"},{"id":"46"}]'::json)) actual on actual.id=expected.id
where
    actual.id is null
;

在上面的查询中,我调用 json_array_elements() 从每个 JSON 字符串中提取单独的数组元素。因为目标 JSON 字符串有对象数组元素,我们必须进一步提取 "id" 值,这可以通过 -> 运算符完成。请注意,value 似乎是分配给 json_array_elements() 函数输出的单列的默认名称。

一个怪癖是提取的 JSON(即 value 值)仍然是 json 类型,而且您似乎无法转换 json 文本直接转换为整数值,例如 intbigint,即使整个 json 文本仅包含有效的 int/bigint,但您可以通过 varchar 到达那里:

select '34'::json::bigint; -- "ERROR:  cannot cast type json to bigint"
select '34'::json::varchar::bigint; -- works, 34

这就是为什么我必须将 json 值转换为 ::varchar::bigint

此外,我不得不使用 substring() 从他们的双引号字符串中提取预期的 ID,否则 ::bigint 转换将不起作用。

此外,次要的一点是,我使用了 bigint 而不是 int,因为那里的数字相当大 (5426565165191919)。

如果您只想 select 一个布尔值,指示是否至少缺少一个预期 ID,您可以将 select 子句更改为:

select
    case when count(expected.id)>0 then true else false end some_missing