从 PostgreSQL 的 json 字段中查询字符串和数字字段的有效方法

Posted

技术标签:

【中文标题】从 PostgreSQL 的 json 字段中查询字符串和数字字段的有效方法【英文标题】:Efficient way of querying string and number field from PostgreSQL's json field 【发布时间】:2021-07-30 13:01:24 【问题描述】:

我们在 postgres 中使用了 json 字段,因为我们有动态字段。字段只能是字符串或数字类型。我们在表中有数十亿行,因此查询工作太慢。我们无法添加索引,因为我们不知道查询中使用的字段名称,并且查询将在运行时动态构建。

表格设计如下,

id - integer
workspace_id - integer
data - json
created_at - timestamp
updated_at - timestamp

json字段中存储的数据如下,

"age": 21, "city": "London", "name": "ABC", "test_filed1": "text",...

字符串字段示例:

SELECT users.* 
FROM users 
WHERE users.workspace_id = 1 
  AND data ->> 'city' = 'London' 
ORDER BY users.id DESC 
LIMIT 50;

数字字段示例:

SELECT users.* 
FROM users 
WHERE users.workspace_id = 1 
  AND CAST(data ->> 'age' AS NUMERIC) = 21 
ORDER BY users.id DESC 
LIMIT 50;

当我们使用->> 运算符获取数据时,它会自动将结果类型转换为字符串。例如,当我得到像data ->> 'age' 这样的年龄时,结果值将被类型转换为'21',尽管它存储为数字值。如果我们需要检查任何与数字相关的条件,尽管我们将年龄数据以数字格式存储在 json 字段中,但我们需要键入 cast(如示例中所述)以检查大于、小于。也用于检查字符串,它也在对::text进行类型转换。

由于我以适当的格式存储了数据(字符串使用引号并将数字存储为不带引号的数字),有没有更好的方法来获取存储在 DB 中的数据而不是类型转换?这样我就可以在没有类型转换的情况下执行与数字相关的条件。

注意:我已经为workspace_id添加了索引。

【问题讨论】:

【参考方案1】:

嗯,->> always 返回一个 text 值 - 这就是它的实现方式(它并不是真正“转换”值)。

JSON 并没有真正的数据类型概念,因此 Postgres 中没有运算符可以在每次使用 JSON 时从 JSON 中提取值时产生不同的数据类型。由于所有内容都可以表示为text,这就是->> 返回的内容。

这是您为数据模型去规范化所付出的代价。

但是,相等比较可以以不同的方式进行(当您将列转换为应该以它开头的jsonb 时)。

SELECT users.* 
FROM users 
WHERE users.workspace_id = 1 
  AND data::jsonb @> '"city": "London"'
ORDER BY users.id DESC 
LIMIT 50;

SELECT users.* 
FROM users 
WHERE users.workspace_id = 1 
  AND data::jsonb @> '"age": 21'
ORDER BY users.id DESC 
LIMIT 50;

@> 可以在 JSONB 值上使用 GIN 索引,例如

create index on users (data::jsonb);

显然,这受制于通常的索引使用规则。并非所有创建的索引都在使用中。

(但最好将data转换为jsonb以避免一直转换)

【讨论】:

无法从 json 迁移到 jsonb,因为我们的代码库和表大小(超过 400GB)非常庞大。如果有其他方法,请提出建议。 @empr:那么你必须忍受演员阵容。

以上是关于从 PostgreSQL 的 json 字段中查询字符串和数字字段的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

如何使用新的 PostgreSQL JSON 数据类型中的字段进行查询?

PostGreSQL Json数据存储和条件查询

PostgreSQL9.4往jsonb字段新增键值SQL怎么写

PostgreSQL的NoSQL特性

检查 json 类型列 PostgreSQL 中是不是存在字段

学说不能返回 json 查询的结果