根据多个 JSONB 列中的值过滤行
Filter rows based on values inside multiple JSONB columns
我正在尝试以 LIKE %str%
方式搜索 table,但是 在 json 内的字段上 json 值跨越多列 。
我有一个 table,它有三个 jsonb 列 change
、previous
和 specific_changes
。正如您想象的那样,内容是 JSON 但 json 的结构无法提前知道,因此 我不能 使用 ->
或 ->>
在查询中像这样:
select * from change_log where change -> 'field' = '"something"'
create table change_log
(
id serial not null
constraint pk_change_log
primary key,
change jsonb not null,
previous jsonb,
changed_at timestamp with time zone default timezone('utc'::text, now()),
specific_changes jsonb
);
INSERT INTO public.change_log (id, change, previous, changed_at, specific_changes) VALUES (1, '{"val": 2, "test": "test", "nested": {"nval": 1}}', 'null', '2020-11-12 16:53:28.827896', '{"val2": "Value2"}');
INSERT INTO public.change_log (id, change, previous, changed_at, specific_changes) VALUES (2, '{"val": "testNewChange", "test": "testChange", "nested": {"key": 1}}', '{"val": "2", "test": "testChange", "nested": {"nval": 1}}', '2020-11-15 12:18:35.021843', '{"new": "testValue"}');
INSERT INTO public.change_log (id, change, previous, changed_at, specific_changes) VALUES (3, '{"val": "newewChange", "test": "changeNew", "nested": {"val": 3}}', '{"val": "testNewChange", "test": "testValue", "nested": {"key": 1}}', '2020-11-15 12:19:40.832843', '{"new": "testChange", "nested": {"val": 1}}');
我的问题是:
给定一个 字符串 的查询将如何 return 来自 change_log table 的所有行其中提到的 3 个 jsonb 列中的任何一个包含具有值 like %string%
.
的任何字段
如何使查询不区分大小写
示例:
INPUT OUTPUT(ids)
"2" (1,2)
"Change" (2,3)
"Chan" (2,3)
"Value" (1,2,3)
EDIT1:我使用的是 postgres 版本 9.6
EDIT2:修复了插入的更改以反映所需的行为
您可以像这样查询
SELECT DISTINCT l.id
FROM change_log l
CROSS JOIN JSONB_EACH_TEXT( l.change ) AS c(e)
CROSS JOIN JSONB_EACH_TEXT( nullif(l.previous, 'null') ) AS p(e)
CROSS JOIN JSONB_EACH_TEXT( l.specific_changes ) AS s(e)
WHERE c.value ~* 'change' OR s.value ~* 'change' OR p.value ~* 'change'
其中 ~*
运算符搜索给定关键字的不区分大小写的匹配项,函数 JSONB_EACH_TEXT()
将最外层的 JSON 对象扩展为一组 key/value 对。
P.S。需要通过将 previous
列的 id 1
值转换为 null
或使用 nullif(l.previous, 'null')
作为第二个 [= 的参数来修复值 'null'
33=]() 在查询中
如果您使用的是 Postgres 12 或更高版本,您可以使用 SQL/JSON 路径表达式:
select *
from change_log
where change @@ '$.** like_regex "change" flag "i"'
or previous @@ '$.** like_regex "change" flag "i"'
or specific_changes @@ '$.** like_regex "change" flag "i"'
旧版本 PostgreSQL 的常用方法是使用 exists
和一些函数,比如
select *
from table_name
where exists (
select 1
from jsonb_each_text(column_name) as t(k,v)
where v ilike '%string%');
对于多个列,可以使用 or
:
select *
from table_name
where
exists (
select 1
from jsonb_each_text(column1) as t(k,v)
where v ilike '%string%') or
exists (
select 1
from jsonb_each_text(column2) as t(k,v)
where v ilike '%string%');
或union
:
select *
from table_name
where
exists (
select 1
from (
select * from jsonb_each_text(column1) union all
select * from jsonb_each_text(column2)) as t(k,v)
where t.v ilike '%string%');
请注意,它不会正确处理嵌套对象,因为它们将作为整个文本进行检查,包括键。
要解决此问题,您需要创建 returns 递归 JSON 中所有值的存储函数。
但这是另一个问题的主题:)
我正在尝试以 LIKE %str%
方式搜索 table,但是 在 json 内的字段上 json 值跨越多列 。
我有一个 table,它有三个 jsonb 列 change
、previous
和 specific_changes
。正如您想象的那样,内容是 JSON 但 json 的结构无法提前知道,因此 我不能 使用 ->
或 ->>
在查询中像这样:
select * from change_log where change -> 'field' = '"something"'
create table change_log
(
id serial not null
constraint pk_change_log
primary key,
change jsonb not null,
previous jsonb,
changed_at timestamp with time zone default timezone('utc'::text, now()),
specific_changes jsonb
);
INSERT INTO public.change_log (id, change, previous, changed_at, specific_changes) VALUES (1, '{"val": 2, "test": "test", "nested": {"nval": 1}}', 'null', '2020-11-12 16:53:28.827896', '{"val2": "Value2"}');
INSERT INTO public.change_log (id, change, previous, changed_at, specific_changes) VALUES (2, '{"val": "testNewChange", "test": "testChange", "nested": {"key": 1}}', '{"val": "2", "test": "testChange", "nested": {"nval": 1}}', '2020-11-15 12:18:35.021843', '{"new": "testValue"}');
INSERT INTO public.change_log (id, change, previous, changed_at, specific_changes) VALUES (3, '{"val": "newewChange", "test": "changeNew", "nested": {"val": 3}}', '{"val": "testNewChange", "test": "testValue", "nested": {"key": 1}}', '2020-11-15 12:19:40.832843', '{"new": "testChange", "nested": {"val": 1}}');
我的问题是:
给定一个 字符串 的查询将如何 return 来自 change_log table 的所有行其中提到的 3 个 jsonb 列中的任何一个包含具有值
的任何字段like %string%
.如何使查询不区分大小写
示例:
INPUT OUTPUT(ids)
"2" (1,2)
"Change" (2,3)
"Chan" (2,3)
"Value" (1,2,3)
EDIT1:我使用的是 postgres 版本 9.6
EDIT2:修复了插入的更改以反映所需的行为
您可以像这样查询
SELECT DISTINCT l.id
FROM change_log l
CROSS JOIN JSONB_EACH_TEXT( l.change ) AS c(e)
CROSS JOIN JSONB_EACH_TEXT( nullif(l.previous, 'null') ) AS p(e)
CROSS JOIN JSONB_EACH_TEXT( l.specific_changes ) AS s(e)
WHERE c.value ~* 'change' OR s.value ~* 'change' OR p.value ~* 'change'
其中 ~*
运算符搜索给定关键字的不区分大小写的匹配项,函数 JSONB_EACH_TEXT()
将最外层的 JSON 对象扩展为一组 key/value 对。
P.S。需要通过将 previous
列的 id 1
值转换为 null
或使用 nullif(l.previous, 'null')
作为第二个 [= 的参数来修复值 'null'
33=]() 在查询中
如果您使用的是 Postgres 12 或更高版本,您可以使用 SQL/JSON 路径表达式:
select *
from change_log
where change @@ '$.** like_regex "change" flag "i"'
or previous @@ '$.** like_regex "change" flag "i"'
or specific_changes @@ '$.** like_regex "change" flag "i"'
旧版本 PostgreSQL 的常用方法是使用 exists
和一些函数,比如
select *
from table_name
where exists (
select 1
from jsonb_each_text(column_name) as t(k,v)
where v ilike '%string%');
对于多个列,可以使用 or
:
select *
from table_name
where
exists (
select 1
from jsonb_each_text(column1) as t(k,v)
where v ilike '%string%') or
exists (
select 1
from jsonb_each_text(column2) as t(k,v)
where v ilike '%string%');
或union
:
select *
from table_name
where
exists (
select 1
from (
select * from jsonb_each_text(column1) union all
select * from jsonb_each_text(column2)) as t(k,v)
where t.v ilike '%string%');
请注意,它不会正确处理嵌套对象,因为它们将作为整个文本进行检查,包括键。
要解决此问题,您需要创建 returns 递归 JSON 中所有值的存储函数。
但这是另一个问题的主题:)