Postgres 9.4 jsonb数组作为表

use*_*708 3 sql postgresql indexing json jsonb

我有一个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"}]}
Run Code Online (Sandbox Code Playgroud)

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"}
Run Code Online (Sandbox Code Playgroud)

但是,如果我需要留在旧结构

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

  2. 什么是得到一个表的3列最快的方式oid,instancevalue.或者更好的是使用oid + instance作为列标题的数据透视表.

对于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';
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 6

询问

您的表定义丢失了.假设:

CREATE TABLE configuration (
  config_id serial PRIMARY KEY
, config jsonb NOT NULL
);
Run Code Online (Sandbox Code Playgroud)

要找到value给定的a 和它的行,oid并且instance:

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'
Run Code Online (Sandbox Code Playgroud)

那是一个隐含的LATERAL联接.相比:

2)什么是得到一个表的3列最快的方法oid,instancevalue.

我想使用jsonb_populate_recordset(),然后您可以在表定义中提供数据类型.假设text所有人:

CREATE TEMP TABLE data_pattern (oid text, value text, instance text);
Run Code Online (Sandbox Code Playgroud)

也可以是一个持久化(非临时)表.这个仅适用于本届会议.然后:

SELECT c.config_id, d.*
FROM   configuration c
     , jsonb_populate_recordset(NULL::data_pattern, c.config->'data') d
Run Code Online (Sandbox Code Playgroud)

就这样.第一个查询被重写:

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';
Run Code Online (Sandbox Code Playgroud)

但这比第一个查询要慢.使用更大表的性能关键是索引支持:

指数

您可以轻松索引标准化(已翻译)表或您在问题中提出的替代布局.索引当前布局不是那么明显,但也可能.为了获得最佳性能,我建议datajsonb_path_ops操作符类的键上使用函数索引.每个文件:

a jsonb_opsjsonb_path_opsGIN索引之间的技术差异在于前者为数据中的每个键和值创建独立的索引项,而后者仅为数据中的每个值创建索引项.

应该是性能的奇迹:

CREATE INDEX configuration_my_idx ON configuration
USING gin ((config->'data') jsonb_path_ops);
Run Code Online (Sandbox Code Playgroud)

人们可能会认为只有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"}]';
Run Code Online (Sandbox Code Playgroud)

请注意所提供的值的JSON数组符号(带有封闭[]).

但是具有键子集的数组元素也可以工作:

SELECT * FROM configuration
WHERE  (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3"
                            , "instance": "0"}]'
Run Code Online (Sandbox Code Playgroud)

困难的部分是结合你看似不吉利的添加谓词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
Run Code Online (Sandbox Code Playgroud)

瞧.

特别指数

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

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

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$
Run Code Online (Sandbox Code Playgroud)

我们可以基于此构建一个功能索引:

CREATE INDEX configuration_conrfig_special_idx ON configuration
USING  gin (f_config_json2arr(config->'data'));
Run Code Online (Sandbox Code Playgroud)

并将查询基于它:

SELECT * FROM configuration
WHERE  f_config_json2arr(config->'data') @> '{1.3.6.1.4.1.7352.3.10.2.5.35.3-0}'::text[]
Run Code Online (Sandbox Code Playgroud)

我们的想法是索引应该大得多,因为它只存储没有键的组合值.的阵列容纳操作者@>本身应该执行类似于jsonb容纳操作者@>.我不希望有太大的不同,但我会非常感兴趣哪个更快.

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

旁白:

  • 我不会将其oid用作列名,因为它也用于Postgres的内部用途.
  • 如果可能的话,我会使用没有JSON的普通规范化表.