请看一下这个查询:
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。
这是您用作从 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'
常量被 Postgrescast (实际上,分配了一个类型)
::text
才能使
->
工作(它需要
text
作为正确的操作数)。
表达式索引:
db<>fiddle 的演示
create index on events ((event_data ->> 'state'));
为了获得更高的性能和灵活性,从 json
切换到
jsonb
并构建一个GIN 索引 是有意义的,它将支持此列上更多类型的查询。
*。
首先,JSON 没有整数。它有“数字”,可能有小数部分。其次,JSON 对象无论如何都不是任何固定类型。数据库中的下一个可能有一个字符串或对象数组的
state
,或者可能根本没有任何
state
。所以,如果你想让 PostgreSQL 知道你承诺那里总是有一个 32 位有符号整数值(或错误),那么你需要告诉它。
* 你可能会说“但我知道它们都是整数”。 (a) 只有在我出现并插入字符串状态之前才是正确的,(b) 对数据库没有用,因为它无法读懂你的想法。