PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Matriz jsonb do Postgres 9.4 como tabela

Consulta


Sua definição de tabela está ausente. Supondo:
CREATE TABLE configuration (
  config_id serial PRIMARY KEY
, config jsonb NOT NULL
);

Para encontrar um value e sua linha para determinado oid e 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'

Isso é um LATERAL implícito Junte-se. Comparar:
  • Consulte elementos de matriz dentro do tipo JSON

2) Qual é a maneira mais rápida de obter uma tabela com 3 colunas de oid , instance e value.

Suponho que use jsonb_populate_recordset() , você poderá fornecer tipos de dados na definição da tabela. Assumindo text para todos:
CREATE TEMP TABLE data_pattern (oid text, value text, instance text);

Também pode ser uma tabela persistente (não temporária). Este é apenas para a sessão atual. Então:
SELECT c.config_id, d.*
FROM   configuration c
     , jsonb_populate_recordset(NULL::data_pattern, c.config->'data') d

Isso é tudo. A primeira consulta reescrita:
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';

Mas isso é mais lento do que a primeira consulta. A chave para o desempenho com uma tabela maior é o suporte ao índice:

Índice


Você pode indexar facilmente a tabela normalizada (traduzida) ou o layout alternativo proposto na pergunta. Indexando seu layout atual não é tão óbvio, mas também possível. Para melhor desempenho, sugiro um índice funcional apenas nos data chave com o jsonb_path_ops classe de operador. Por documentação:

A diferença técnica entre um jsonb_ops e um jsonb_path_ops GINindex é que o primeiro cria itens de índice independentes para cada chave e valor nos dados, enquanto o último cria itens de índice apenas para cada valor nos dados.

Isso deve fazer maravilhas para desempenho:
CREATE INDEX configuration_my_idx ON configuration
USING gin ((config->'data') jsonb_path_ops);

Pode-se esperar que apenas uma correspondência completa para um elemento de matriz JSON funcione, como:
SELECT * FROM configuration
WHERE  (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3"
                            , "instance": "0", "value": "1234"}]';

Observe a notação de matriz JSON (com incluindo [] ) do valor fornecido, isso é obrigatório.

Mas elementos de matriz com um subconjunto de chaves trabalhar também:
SELECT * FROM configuration
WHERE  (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3"
                            , "instance": "0"}]'

A parte difícil é incorporar seu predicado adicionado aparentemente insuspeito value <> '1' . Deve-se tomar cuidado para aplicar todos os predicados ao mesmo elemento de matriz. Você pode combinar isso com a primeira consulta:
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

Voilá.

Índice especial


Se sua tabela for enorme, o tamanho do índice pode ser um fator decisivo. Você pode comparar o desempenho desta solução especial com um índice funcional:

Esta função extrai um array Postgres de oid-instance combinações de um determinado jsonb valor:
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$

Podemos construir um índice funcional com base nisso:
CREATE INDEX configuration_conrfig_special_idx ON configuration
USING  gin (f_config_json2arr(config->'data'));

E baseie a consulta nele:
SELECT * FROM configuration
WHERE  f_config_json2arr(config->'data') @> '{1.3.6.1.4.1.7352.3.10.2.5.35.3-0}'::text[]

A ideia é que o índice seja substancialmente menor porque armazena apenas os valores combinados sem chaves. A matriz operador de contenção @> em si deve ter um desempenho semelhante ao operador de contenção jsonb @> . Eu não espero uma grande diferença, mas eu estaria muito interessado que é mais rápido.

Semelhante à primeira solução nesta resposta relacionada (mas mais especializada):
  • Índice para encontrar um elemento em uma matriz JSON

Apartes:

  • Eu não usaria oid como nome da coluna, pois também é usado para fins internos no Postgres.
  • Se possível, eu usaria uma tabela normalizada simples sem JSON.