我应该选择 ->> 还是 @>
Should I prefer ->> or @>
在 JSONB 列的 postgreSQL 中,我可以 运行 两个具有相同 (?) 结果的查询。
查询 1:
SELECT * FROM a WHERE b->>'c' = 'lorem';
查询 2:
SELECT * FROM a WHERE b @> '{"c": "lorem"}';
就性能和语义而言(也许还有一些我在这里没有看到的其他考虑因素),我应该使用什么查询来搜索 "the items from a
where c
is lorem
"?
哪种解决方案对您来说更好是一个品味问题,您可以自己判断。它们在我看来几乎一样。
说到性能,建议大家自己测试一下。
在PL/pgSQL中写一个DO
语句,循环执行100000次相同的操作,然后使用psql
命令\timing
或类似的东西,看看需要多长时间。重复测试几次以检查结果是否可重现。很可能差异(如果有的话)在噪音中消失了。
与 Laurenz 有着相同的想法。尽管顶级键值对的数量很多,但它们的表现似乎相同:
t=> do
t-> $$
t$> declare
t$> _i int;
t$> _j jsonb;
t$> begin
t$> with n as (select generate_series(1,9999,1) g) select concat('{',string_agg(concat('"a',g,'":22'),','),',"c": "lorem"}')::jsonb into _j from n;
t$> for _r in 1..999999 loop
t$> select 1 into _i where _j @> '{"c": "lorem"}';
t$> end loop;
t$> end;
t$> $$
t-> ;
DO
Time: 2406.016 ms
t=>
t=> do
t-> $$
t$> declare
t$> _i int;
t$> _j jsonb;
t$> begin
t$> with n as (select generate_series(1,9999,1) g) select concat('{',string_agg(concat('"a',g,'":22'),','),',"c": "lorem"}')::jsonb into _j from n;
t$> for _r in 1..999999 loop
t$> select 1 into _i where _j->>'c' = 'lorem';
t$> end loop;
t$> end;
t$> $$
t-> ;
DO
Time: 2799.750 ms
这取决于 what indexes 您是否拥有或想要添加(如果您想要使用索引)。以及您想在 jsonb
类型列上执行的其他查询。
WHERE b->>'c' = 'lorem'
查询将受益于 (b->>'c')
表达式 上的索引,而
WHERE b @> '{"c": "lorem"}'
查询将受益于 (b)
列 上的 GIN 索引,但反之则不然。
第一种形式可能会产生更小、更有效的索引,但仅适用于这种特殊情况。如果你还想查询 b
的其他属性,GIN 索引会更有帮助。
如果您根本不想使用索引,那真的只是个人喜好问题。
旁注: 上面的解决方案在处理 NULL
s 时略有不同:
WHERE b @> '{"c": null}'
将 select 行,并且 仅当 c
属性 具有 JSON null
其中的价值,而
WHERE (b ->> 'c') IS NULL
如果 c
属性 中有 JSON null
值, 将 select 行, 或 c
属性 根本没有在行中定义。
此外,
WHERE (b ->> 'c') = NULL
不会 select 任何行,因为 NULL
的标准兼容处理(表达式 (b ->> 'c') = NULL
总是评估为 NULL
-- 或 UNKNOWN
在 BOOLEAN
类型 -- 中,在 WHERE
谓词的上下文中总是 falsy)。
在 JSONB 列的 postgreSQL 中,我可以 运行 两个具有相同 (?) 结果的查询。
查询 1:
SELECT * FROM a WHERE b->>'c' = 'lorem';
查询 2:
SELECT * FROM a WHERE b @> '{"c": "lorem"}';
就性能和语义而言(也许还有一些我在这里没有看到的其他考虑因素),我应该使用什么查询来搜索 "the items from a
where c
is lorem
"?
哪种解决方案对您来说更好是一个品味问题,您可以自己判断。它们在我看来几乎一样。
说到性能,建议大家自己测试一下。
在PL/pgSQL中写一个DO
语句,循环执行100000次相同的操作,然后使用psql
命令\timing
或类似的东西,看看需要多长时间。重复测试几次以检查结果是否可重现。很可能差异(如果有的话)在噪音中消失了。
与 Laurenz 有着相同的想法。尽管顶级键值对的数量很多,但它们的表现似乎相同:
t=> do
t-> $$
t$> declare
t$> _i int;
t$> _j jsonb;
t$> begin
t$> with n as (select generate_series(1,9999,1) g) select concat('{',string_agg(concat('"a',g,'":22'),','),',"c": "lorem"}')::jsonb into _j from n;
t$> for _r in 1..999999 loop
t$> select 1 into _i where _j @> '{"c": "lorem"}';
t$> end loop;
t$> end;
t$> $$
t-> ;
DO
Time: 2406.016 ms
t=>
t=> do
t-> $$
t$> declare
t$> _i int;
t$> _j jsonb;
t$> begin
t$> with n as (select generate_series(1,9999,1) g) select concat('{',string_agg(concat('"a',g,'":22'),','),',"c": "lorem"}')::jsonb into _j from n;
t$> for _r in 1..999999 loop
t$> select 1 into _i where _j->>'c' = 'lorem';
t$> end loop;
t$> end;
t$> $$
t-> ;
DO
Time: 2799.750 ms
这取决于 what indexes 您是否拥有或想要添加(如果您想要使用索引)。以及您想在 jsonb
类型列上执行的其他查询。
WHERE b->>'c' = 'lorem'
查询将受益于 (b->>'c')
表达式 上的索引,而
WHERE b @> '{"c": "lorem"}'
查询将受益于 (b)
列 上的 GIN 索引,但反之则不然。
第一种形式可能会产生更小、更有效的索引,但仅适用于这种特殊情况。如果你还想查询 b
的其他属性,GIN 索引会更有帮助。
如果您根本不想使用索引,那真的只是个人喜好问题。
旁注: 上面的解决方案在处理 NULL
s 时略有不同:
WHERE b @> '{"c": null}'
将 select 行,并且 仅当 c
属性 具有 JSON null
其中的价值,而
WHERE (b ->> 'c') IS NULL
如果 c
属性 中有 JSON null
值, 将 select 行, 或 c
属性 根本没有在行中定义。
此外,
WHERE (b ->> 'c') = NULL
不会 select 任何行,因为 NULL
的标准兼容处理(表达式 (b ->> 'c') = NULL
总是评估为 NULL
-- 或 UNKNOWN
在 BOOLEAN
类型 -- 中,在 WHERE
谓词的上下文中总是 falsy)。