在一条SELECT语句中 Select 多个包含jsonb_array_elements的列是否安全?是否可以保证一列中展开元素的顺序与第二列中的顺序相同?

Example:我的表包含一个列data,该列包含一个jsonarray.该数组的每个元素都是一个具有两个属性(idname)的对象:

                                data                                
--------------------------------------------------------------------
 [{"id": "11", "name": "entry11"}, {"id": "12", "name": "entry12"}]
 [{"id": "21", "name": "entry21"}, {"id": "22", "name": "entry22"}]

如果我参选

with my_table(data) as (values
    ('[{"id": "11", "name":"entry11"},{"id": "12", "name": "entry12"}]'::jsonb),
    ('[{"id": "21", "name":"entry21"},{"id": "22", "name": "entry22"}]'::jsonb)
)
select 
  jsonb_array_elements(data)->'id' as id,
  jsonb_array_elements(data)->'name' as name
from my_table
;

我得到了预期的结果:

  id  |   name    
------+-----------
 "11" | "entry11"
 "12" | "entry12"
 "21" | "entry21"
 "22" | "entry22"

My Question:由于jsonb_array_elements的两个调用由数据库独立处理,是否存在name entry22最终与id 21排在同一行的风险?我的实验(也是用更大的桌子)表明,它总是有效的.但由于关系数据库通常是don't have a stable ordering of rows个,我想知道我是否可以依赖这个结果.

推荐答案

是的,有保证的.但这是一个很弱的问题.

idname将保持同步(来自相同的嵌套对象),因为这两个集返回函数是以"lockStep"计算的.The manual:

如果查询的SELECT中有多个集合返回函数 列表中,该行为类似于将 函数合并到一个包含LATERAL ROWS FROM( ... ) FROM个子句的项中. 对于基础查询中的每一行,都有一个使用 每个函数的第一个结果,然后是使用 第二个结果,以此类推.如果一些集合返回函数 生成的输出比其他输出更少,则空值将替换 丢失的数据,因此为一个 基础行与集合返回函数的相同, 输出最多.这样,set-returning functions run “in lockstep”直到它们都用完了,然后继续执行 与下一个底层行.

Bold强调我的.

由于您的两个调用都保证返回相同数量的行数,因此这甚至可以在Postgres 10之前可靠地工作(在Postgres 10中,SELECT列表中的多个集返回函数的行为已被 retrofit (清理)).请参见:

json不同,jsonb具有确定的嵌套元素排序顺序.但这里唯一相关的方面是数组项的顺序,这始终是重要的,在jsonjsonb中都保留了下来.

优化查询

详细阐述了我们刚刚了解到的内部工作原理,您的查询:

SELECT jsonb_array_elements(data) -> 'id'   AS id
     , jsonb_array_elements(data) -> 'name' AS name
FROM   tbl;

...相当于:

SELECT t.id, x.i->'id' AS id, x.n->'name' AS name
FROM   tbl t
CROSS  JOIN LATERAL ROWS FROM (jsonb_array_elements(t.data), jsonb_array_elements(t.data)) x(i, n);

更明显的是,它应该简化为子查询中的单个函数调用.则字段不能从一开始就不同步:

SELECT t.id, x->'id' AS id, x->'name' AS name
FROM   tbl t
CROSS  JOIN LATERAL jsonb_array_elements(t.data) x;

或者,使用最少的语法:

SELECT id, x->'id' AS id, x->'name' AS name
FROM   tbl, jsonb_array_elements(data) x;

fiddle个个

当从同一函数调用返回多个单独的字段时,将函数调用移动到子查询,如图所示.否则,你就会冒着重复判断的风险(代价高昂).请参见:

True issue with (multiple) SRF in the SELECT list

以上均为Atrap号港湾.如果SELECT列表中的SRF没有返回行(就像jsonb_array_elements()发现空数组时),则从结果中删除该行.当SELECT列表中有多个SRF时,当所有SRF都为空时,该行将被删除.这可能会带来令人惊讶的结果.从我的经验来看,很少有人意识到其中的微妙含义.

如果这种副作用是有意的,如上所述,它的拼写要清楚得多,即CROSS JOIN.

如果该副作用不是故意的,并且您更愿意保留所有行,请改用LEFT JOIN LATERAL ... ON true:

SELECT t.id, x->'id' AS id, x->'name' AS name
FROM   tbl t
LEFT   JOIN LATERAL jsonb_array_elements(t.data) x ON true;

fiddle个个

Use this query美元,除非你知道得更清楚.请参见:

Aside: jsonb_populate_recordset() for this particular query

对于使用jsonb_populate_recordset()的特定查询,有一种更有效的方法:更快,并使用隐式类型转换.

如果没有管件组合类型,请创建一次:

CREATE TYPE my_rowtype AS (id int, name text);

然后:

SELECT t.id, x.id, x.name
FROM   tbl t
LEFT   JOIN LATERAL jsonb_populate_recordset(null::my_rowtype, t.data) x ON true

fiddle

相关:

Postgresql相关问答推荐

DBT-DBT依赖于未找到的源

为什么 Postgres 中的 now() 和 now()::timestamp 对于 CET 和 CEST 时区如此错误?

安装age-installation时出错

即使存在 GIN 索引,整理默认的类似查询也无法执行

如何在docker-compose中使用docker secrets设置ConnectionString?

PostgreSQL 函数按名称传递参数

PostgreSQL:please specify covering index name是什么意思

更新列或列或列时触发触发

在 postgres 中删除所有共享相同前缀的表

需要将整个 postgreSQL 数据库加载到 RAM 中

Postgres 中的相似函数与 pg_trgm

Postgresql:在插入列时(或之前)自动小写文本

从没有行的计数中获取 0 值

Redis 可以写出到像 PostgreSQL 这样的数据库吗?

如何判断 PostgreSQL 事务中的待处理操作

错误:表tablename上的更新或删除违反外键约束

为什么是||在 PostgreSQL/Redshift 中用作字符串连接

使用 Homebrew 安装 icu4c 版本 63

Rails + Postgres fe_sendauth:no password supplied

在 postgres 中导出为 CSV 并使用 GZIP 压缩