避免对从 JSON 检索的数字字段进行无意义的 ::int 转换

问题描述 投票:0回答:2

请看一下这个查询:

SELECT COUNT(*) FROM events e WHERE (e.event_data -> 'state')::int = -1;

查询有效,一切都正常,但是......

这就是我从数据库读取 JSON 对象时的样子:

{
  "eventUID": "3ea6baf7-8772-48a5-a32b-b00901534025",
  "data": {<some-data>},
  "state": 1
}

如您所见,

'state'
int
类型。

EXPLAIN ANALYZE
给我看看这个:

Aggregate  (cost=15.08..15.09 rows=1 width=8) (actual time=0.042..0.076 rows=1 loops=1)
  ->  Seq Scan on events e  (cost=0.00..15.07 rows=1 width=0) (actual time=0.018..0.027 rows=0 loops=1)
        Filter: (((event_data -> 'state'::text))::integer = -1)
Planning Time: 0.139 ms
Execution Time: 0.198 ms

显然 Postgres 将已经存在的

int
字段转换为
text
,然后再次将其转换为
int

那么是否可以避免强制转换,使

select
看起来像这样(我知道这是无效的
select
):

SELECT COUNT(*) FROM events e WHERE e.event_data -> 'state' = -1;

使用 PostgreSQL 16。

sql json postgresql jsonb postgresql-json
2个回答
3
投票

这是您用作从 JSON 中提取的键的

'state'
常量,该常量被转换为
::text
。与 JSON 值上的
->
运算符
结合使用,可获取 JSON 输出:

json ->
text
→ json

jsonb ->
text
→ jsonb

使用给定的键提取 JSON 对象字段。

'{"a": {"b":"foo"}}'::json -> 'a' → {"b":"foo"}

实际上,您可以像这样阅读代码:

SELECT COUNT(*) 
FROM events e 
WHERE (e.event_data --no cast necessary, it's a known `json`
       -> --there are multiple operators like this
       'state'--this is an `unknown` constant, but it makes sense to interpret
              --it as `text`, because that enables a `json->text` operator
       )--the operation inside the parentheses yields a `json`
    ::int--this only works if you're sure the `json` inside is a number 
         --and if you use ->>, not a -> in there, or go through ::text
    = -1;

如果您想要 SQL

integer
而不是带有 number
 原语
的 JSON
,那么最后的
::int
 转换是必要的。

postgres 将已经是 int 的字段转换为文本,然后再次将其转换为 int

事实并非如此。只有

'state'

 常量被 Postgres 
cast (实际上,分配了一个类型) ::text
 才能使 
->
 工作(它需要 
text
 作为正确的操作数)。


如果您希望加快此特定查询的速度,可以添加

表达式索引:
db<>fiddle 的演示

create index on events ((event_data ->> 'state'));
为了获得更高的性能和灵活性,从 

json

 切换到 
jsonb
 并构建一个 
GIN 索引 是有意义的,它将支持此列上更多类型的查询。


0
投票
它不是整数,也不可能是

*

首先,JSON 没有整数。它有“数字”,可能有小数部分。

其次,JSON 对象无论如何都不是任何固定类型。数据库中的下一个可能有一个字符串或对象数组的

state

,或者可能根本没有任何 
state

所以,如果你想让 PostgreSQL 知道你承诺那里总是有一个 32 位有符号整数值(或错误),那么你需要告诉它。

* 你可能会说“但我知道它们都是整数”。 (a) 只有在我出现并插入字符串状态之前才是正确的,(b) 对数据库没有用,因为它无法读懂你的想法。

© www.soinside.com 2019 - 2024. All rights reserved.