我有一个json数组,其中包含大约1000个结构"oid: aaa, instance:bbb, value:ccc"的元素。
{"_id": 37637070
, "data": [{"oid": "11.5.15.1.4", "value": "1", "instance": "1.1.4"}
, {"oid": "11.5.15.1.9", "value": "17", "instance": "1.1.4"}
, {"oid": "12.5.15.1.5", "value": "0.0.0.0", "instance": "0"}]}
oid
和instance
对于每个 JSON 数组都是唯一的。 如果让我选择更改结构,我会将格式更改为 key:value:
{"11.5.15.1.4-1.1.4":"1", "11.5.15.1.9-1.1.4": "17", "12.5.15.1.5-0": "0.0.0.0"}
但是,如果我需要保持旧结构
从阵列中获取特定
oid
的最快方法是什么?获得包含 3 列
oid
、instance
和value
的表的最快方法是什么?或者更好的是将oid +实例作为列标题的数据透视表。
对于 2.我尝试了以下方法,但在大桌子上很慢:
select *
from (
select a->>'oid' oid, a->>'instance' instance, a->>'value' value1, id
from (
select jsonb_array_elements(config#>'{data}') a, id
from configuration
) b
) c
where oid = '1.3.6.1.4.1.7352.3.10.2.5.35.3' and instance = '0' and value1 <> '1';
查询
缺少表定义。若:
CREATE TABLE configuration (
config_id serial PRIMARY KEY
, config jsonb NOT NULL
);
要查找给定oid
和instance
的 a value
及其行:
SELECT c.config_id, d->>'value' AS value
FROM configuration c
, jsonb_array_elements(config->'data') d -- default col name is "value"
WHERE d->>'oid' = '1.3.6.1.4.1.7352.3.10.2.5.35.3'
AND d->>'instance' = '0'
AND d->>'value' <> '1';
这是一个隐式LATERAL
联接。比较:
- 查询 JSON 类型中的数组元素
- 获得包含 3 列
oid
、instance
和value.
的表格的最快方法是什么
我想要使用jsonb_populate_recordset()
,那么您可以在表定义中提供数据类型。假设所有人都text
:
CREATE TEMP TABLE data_pattern (oid text, value text, instance text);
也可以是持久(非临时)表。这仅适用于当前会话。然后:
SELECT c.config_id, d.*
FROM configuration c
, jsonb_populate_recordset(NULL::data_pattern, c.config->'data') d;
就这样。重写了第一个查询:
SELECT c.config_id, d.*
FROM configuration c
, jsonb_populate_recordset(NULL::data_pattern, c.config->'data') d
WHERE d.oid = '1.3.6.1.4.1.7352.3.10.2.5.35.3'
AND d.instance = '0'
AND d.value <> '1';
但这比第一个查询慢。使用更大的表实现性能的关键是索引支持:
指数
您可以轻松地索引规范化(翻译)表或您在问题中建议的替代布局。索引当前布局并不那么明显,但也是可能的。为了获得最佳性能,我建议仅对data
键使用jsonb_path_ops
运算符类进行功能索引。根据文档:
jsonb_ops
和jsonb_path_ops
杜松子酒之间的技术区别索引是前者为每个键创建独立的索引项和数据中的值,而后者仅为数据中的每个值。
这应该为性能创造奇迹:
CREATE INDEX configuration_my_idx ON configuration
USING gin ((config->'data') jsonb_path_ops);
人们可能认为只有 JSON 数组元素的完全匹配才能工作,例如:
SELECT * FROM configuration
WHERE (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3"
, "instance": "0", "value": "1234"}]';
请注意所提供值的 JSON 数组表示法(带封闭[]
):这是必需的。
但是带有键子集的数组元素也可以工作:
SELECT * FROM configuration
WHERE (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3"
, "instance": "0"}]'
困难的部分是合并您添加的谓词value <> '1'
。必须注意将所有谓词应用于同一数组元素。您可以将其与第一个查询结合使用:
SELECT c.*, d->>'value' AS value
FROM configuration c
, jsonb_array_elements(config->'data') d
WHERE (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3", "instance": "0"}]'
AND d->>'oid' = '1.3.6.1.4.1.7352.3.10.2.5.35.3' -- must be repeated
AND d->>'instance' = '0' -- must be repeated
AND d->>'value' <> '1' -- here we can rule out
瞧。
特殊指标
如果您的表很大,索引大小可能是一个决定性因素。您可以将此特殊解决方案的性能与功能指数进行比较:
此函数从给定的 jsonb
值中提取 Oid-实例组合的 Postgres 数组:
CREATE OR REPLACE FUNCTION f_config_json2arr(_j jsonb)
RETURNS text[]
LANGUAGE sql IMMUTABLE PARALLEL SAFE AS
$func$
SELECT ARRAY(
SELECT (elem->>'oid') || '-' || (elem->>'instance')
FROM jsonb_array_elements(_j) elem
)
$func$;
我们可以基于此构建函数索引:
CREATE INDEX configuration_conrfig_special_idx ON configuration
USING gin (f_config_json2arr(config->'data'));
并基于它进行查询:
SELECT * FROM configuration
WHERE f_config_json2arr(config->'data') @> '{1.3.6.1.4.1.7352.3.10.2.5.35.3-0}'::text[]
这个想法是索引应该小得多,因为它只存储组合值而不存储键。数组包含运算符@>
本身应执行类似于 jsonb 包含运算符@>
。我预计不会有很大的不同,但我会非常感兴趣哪个更快。
与此相关答案中的第一个解决方案类似(但更专业):
- 用于在 JSON 数组中查找元素的索引
旁白
- 我不会将
oid
用作列名,因为它也用于 Postgres 中的内部目的。 - 如果可能的话,我会使用没有 JSON 的普通规范化表。