jsonb[] vs jsonb,其中 json 是一个数组

Posted

技术标签:

【中文标题】jsonb[] vs jsonb,其中 json 是一个数组【英文标题】:jsonb[] vs jsonb where json is an array 【发布时间】:2019-06-17 04:00:40 【问题描述】:

我有一个 Postgres 表,mytable,其中一个字段如下:

myField JSONB[] NOT NULL

让我们假设上述 json 是这种形式:

"letter":"A", "digit":30

我应该使用哪些查询:

提取digit 值的数组? 提取一个包含digit值的json数组? 提取digit 值的数组,其中digit > 20? 提取digit 值的json 数组,其中digit > 20?

如果我将数据存储为 json 而 json 是一个列表,那么上述查询将如何变化?

我还能进行上述所有查询吗? 性能差异是什么? 我什么时候应该选择其中之一?

【问题讨论】:

你尝试了什么? 【参考方案1】:

让我们创建一个表,其中包含一个名为 pg_array 的类型为 jsonb[] 的列将存储一个数组 JSON 对象,以及一个类型为 jsonb 的列 json_array 将存储一个 JSON 对象数组:

CREATE TABLE mytable (id int, pg_array jsonb[], json_array jsonb);
INSERT INTO mytable VALUES
    (1, ARRAY['"letter":"A", "digit":30', '"letter":"B", "digit":31']::jsonb[], '["letter":"A", "digit":30,"letter":"B", "digit":31]'),
    (2, ARRAY['"letter":"X", "digit":40', '"letter":"Y", "digit":41']::jsonb[], '["letter":"X", "digit":40,"letter":"Y", "digit":41]');

这两种方法的查询看起来非常相似,因为我们将处理单个数组元素,这意味着我们必须再次取消嵌套和聚合。

取消嵌套pg_array 并获取每个jsonb 对象:

SELECT unnest(pg_array);

取消嵌套json_array 并获取每个jsonb 对象:

SELECT jsonb_array_elements(json_array);

这是唯一的区别。因此,下面的查询看起来几乎相同。

关于你的第一组问题:

提取一个数字值数组?

db=# SELECT array_agg((x->>'digit')::int) FROM mytable, unnest(pg_array) x GROUP BY id;
 array_agg
-----------
 40,41
 30,31
(2 rows)
db=# SELECT array_agg((x->>'digit')::int) FROM mytable, jsonb_array_elements(json_array) x GROUP BY id;
 array_agg
-----------
 40,41
 30,31
(2 rows)

提取一个包含数字值的 json 数组?

db=# SELECT jsonb_agg((x->>'digit')::int) FROM mytable, unnest(pg_array) x GROUP BY id;
 jsonb_agg
-----------
 [40, 41]
 [30, 31]
(2 rows)
db=# SELECT jsonb_agg((x->>'digit')::int) FROM mytable, jsonb_array_elements(json_array) x GROUP BY id;
 jsonb_agg
-----------
 [40, 41]
 [30, 31]
(2 rows)

提取 digit > 20 的数字值数组?

(我在这里使用 30 而不是 20。)

db=# SELECT array_agg((x->>'digit')::int) FROM mytable, unnest(pg_array) x WHERE (x->>'digit')::int > 30 GROUP BY id;
 array_agg
-----------
 40,41
 31
(2 rows)
db=# SELECT array_agg((x->>'digit')::int) FROM mytable, jsonb_array_elements(json_array) x WHERE (x->>'digit')::int > 30 GROUP BY id;
 array_agg
-----------
 40,41
 31
(2 rows)

提取数字> 20的数字值的json数组?

(我在这里使用 30 而不是 20。)

db=# SELECT jsonb_agg((x->>'digit')::int) FROM mytable, unnest(pg_array) x WHERE (x->>'digit')::int > 30 GROUP BY id;
 jsonb_agg
-----------
 [40, 41]
 [31]
(2 rows)
db=# SELECT jsonb_agg((x->>'digit')::int) FROM mytable, jsonb_array_elements(json_array) x WHERE (x->>'digit')::int > 30 GROUP BY id;
 jsonb_agg
-----------
 [40, 41]
 [31]
(2 rows)

第二组问题:

我还能进行以上所有查询吗?

如上所示,是的。

性能有何不同?

这归结为unnestjsonb_array_elements 的性能差异。让我们将其与包含具有 1,000,000 个 JSON 对象的数组的单行进行比较:

TRUNCATE mytable;
INSERT INTO mytable
SELECT 1, array_agg(o), jsonb_agg(o)
FROM (SELECT jsonb_build_object('letter', 'A', 'digit', i) o FROM generate_series(1, 1000000) i) x;
phil=# EXPLAIN ANALYZE SELECT unnest(pg_array) FROM mytable;
                                                QUERY PLAN
-----------------------------------------------------------------------------------------------------------
 ProjectSet  (cost=0.00..35.88 rows=5000 width=32) (actual time=33.357..120.393 rows=1000000 loops=1)
   ->  Seq Scan on mytable  (cost=0.00..10.50 rows=50 width=626) (actual time=0.010..0.013 rows=1 loops=1)
 Planning time: 0.050 ms
 Execution time: 175.670 ms
(4 rows)

phil=# EXPLAIN ANALYZE SELECT jsonb_array_elements(json_array) FROM mytable;
                                                QUERY PLAN
-----------------------------------------------------------------------------------------------------------
 ProjectSet  (cost=0.00..35.88 rows=5000 width=32) (actual time=257.313..399.883 rows=1000000 loops=1)
   ->  Seq Scan on mytable  (cost=0.00..10.50 rows=50 width=721) (actual time=0.010..0.014 rows=1 loops=1)
 Planning time: 0.047 ms
 Execution time: 455.275 ms
(4 rows)

由此看来,unnestjsonb_array_elements 快 2.5 倍左右。

我什么时候应该选择一个而不是另一个?

我假设您的数据集不足以让unnestjsonb_array_elements 之间的性能差异发挥作用。因此,我只会选择在数据方面更有意义的内容。我倾向于使用jsonb[],因为它更清楚地表明您将拥有一组 json 对象。

【讨论】:

以上是关于jsonb[] vs jsonb,其中 json 是一个数组的主要内容,如果未能解决你的问题,请参考以下文章

Postgres jsonb null vs 空对象查询性能

PostgreSQL 中 JSONB[] (JSONB ARRAY) 数据类型有啥用?

使用 JPA 原生查询在 jsonb(JSON 数组)列中查询

在mysql中使用&搜索jsonb数据

PostgreSQL 未对 JSONB 上的 GIN 索引使用索引扫描

JSON 与 JSONB