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 deoid
,instance
evalue.
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 umjsonb_ops
e umjsonb_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.