Postgres 9.4 jsonb 数组为 table

Postgres 9.4 jsonb array as table

我有一个 json 数组,其中包含结构 "oid: aaa, instance:bbb, value:ccc" 的大约 1000 个元素。

{"_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"}]}

oidinstance 在每个 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"}

但是,如果我需要继续使用旧结构

  1. 从数组中获取特定 oid 的最快方法是什么?

  2. 获得具有 oidinstancevalue 3 列的 table 的最快方法是什么?甚至更好的是将 oid+instance 作为列 header.

  3. 的枢轴 table

对于 2。我尝试了以下方法,但在大型 table 上速度很慢:

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';

查询

您的 table 定义缺失。假设:

CREATE TABLE configuration (
  config_id serial PRIMARY KEY
, config jsonb NOT NULL
);

找到给定 oidinstancevalue 及其行:

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 连接。比较:

  • Query for array elements inside JSON type

2) What is the fastest way to get a table with 3 columns of oid, instance and value.

我假设使用jsonb_populate_recordset(),那么你可以在table定义中提供数据类型。假设所有人 text

CREATE TEMP TABLE data_pattern (oid text, value text, instance text);

也可以是持久化的(非临时)table。这一个仅适用于当前会话。那么:

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';

但这比第一个查询。更大 table 性能的关键是索引支持:

索引

您可以轻松索引规范化(翻译)table 或您在问题中提出的替代布局。索引您的 当前布局 不是很明显,但也是可能的。为了获得最佳性能,我建议使用 jsonb_path_ops 运算符 class 在 data 键上使用函数索引。 Per documentation:

The technical difference between a jsonb_ops and a jsonb_path_ops GIN index is that the former creates independent index items for each key and value in the data, while the latter creates index items only for each value in the data.

应该创造奇迹 性能:

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'。必须注意将所有谓词应用于 same 数组元素。您可以将其与第一个查询结合使用:

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

瞧瞧。

特殊索引

如果您的 table 很大,索引大小可能是一个决定因素。您可以将此特殊解决方案的性能与功能索引进行比较:

此函数从给定的 jsonb 值中提取 oid-instance 组合的 Postgres 数组:

CREATE OR REPLACE FUNCTION f_config_json2arr(_j jsonb)
  RETURNS text[] LANGUAGE sql IMMUTABLE 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[]

想法是索引应该小得多,因为它只存储没有键的组合值。 array containment operator @> itself should perform similar to the jsonb containment operator @>。我不希望有太大的不同,但我会非常感兴趣哪个更快。

类似于此相关答案中的第一个解决方案(但更专业):

  • Index for finding an element in a JSON array

旁白:

  • 我不会使用 oid 作为列名,因为它在 Postgres 中也用于内部用途。
  • 如果可能的话,我会使用没有 JSON 的普通标准化 table。