Como obter um objeto específico da matriz jsonb no PostgreSQL?


15

Eu tenho um campo chamado 'usuário' que contém uma matriz json que mais ou menos se parece com isso:

"user":

[{ "_id" : "1", "count" : "4" }, { "_id" : "3", "count": "4"}]

Agora eu quero uma consulta como:

select count from tablename where id = "1"

Não consigo obter o campo específico countde uma matriz de objetos json no PostgreSQL 9.4.

Respostas:


17

Seria muito mais eficiente armazenar seus valores em um esquema normalizado. Dito isto, você também pode fazê-lo funcionar com sua configuração atual.

Premissas

Assumindo esta definição de tabela:

CREATE TABLE tbl (tbl_id int, usr jsonb);

"usuário" é uma palavra reservada e requer que aspas duplas sejam usadas como nome da coluna. Não faça isso. Eu uso em usrvez disso.

Inquerir

A consulta não é tão trivial quanto os comentários (agora excluídos) fizeram parecer:

SELECT t.tbl_id, obj.val->>'count' AS count
FROM   tbl t
JOIN   LATERAL jsonb_array_elements(t.usr) obj(val) ON obj.val->>'_id' = '1'
WHERE  t.usr @> '[{"_id":"1"}]';

Existem 3 etapas básicas :

1. Identifique linhas qualificadas baratas

WHERE t.usr @> '[{"_id":"1"}]'identifica linhas com objeto correspondente na matriz JSON. A expressão pode usar um índice GIN genérico na jsonbcoluna ou um com a classe de operador mais especializada jsonb_path_ops:

CREATE INDEX tbl_usr_gin_idx ON tbl USING gin (usr jsonb_path_ops);

A WHEREcláusula adicionada é logicamente redundante , mas é necessária para usar o índice. A expressão na cláusula de junção impõe a mesma condição, mas somente após desinvestir a matriz em todas as linhas qualificadas até o momento. Com o suporte ao índice, o Postgres processa apenas linhas que contêm um objeto qualificado para começar. Não importa muito com tabelas pequenas, faz uma enorme diferença com tabelas grandes e com poucas linhas de qualificação.

Relacionado:

2. Identifique os objetos correspondentes na matriz

Desnaturar com jsonb_array_elements(). ( unnest()só é bom para os tipos de array do Postgres.) Como estamos interessados ​​apenas na correspondência de objetos, filtre a condição de junção imediatamente.

Relacionado:

3. Extrair valor para chave aninhada 'count'

Depois de objetos de qualificação foram extraídos, simplesmente: obj.val->>'count'.


2
De onde obj(value)vem isso? Está no LATERAL JOIN, no jsonb_array_elementsou em outro lugar?
Tyler DeWitt

Parece que a formatação pode ter sido estragada. Estou lendo corretamente isso JOIN LATERAL jsonb_array_elements(t.usr) obj(value) is short for JOIN LATERAL jsonb_array_elements(t.usr) AS obj(value)e isso obj(value)é um alias de tabela e coluna? Neste exemplo, se objé um alias de tabela, para que é um alias? O conjunto retornou de jsonb_array_elements?
Tyler DeWitt

11
sim e sim. eu removi meu comentário embaralhado.
Erwin Brandstetter

É necessário usar o alias da coluna? Nos meus testes, JOIN LATERAL jsonb_array_elements(t.usr) obj ON obj->>'_id' = '1'teve o mesmo efeito (depois de atualizar a instrução select para usar em valuevez de val). Parece que jsonb_array_elements(t.usr)retorna uma tabela com apenas uma coluna. O postgres é inteligente e percebe que obj ->>é o mesmo obj.val ->>?
Tyler DeWitt

Com apenas uma coluna, o Postgres usa um determinado alias como tabela e nome da coluna. Estou apenas sendo explícito, pois há muitas funções de retorno de conjunto que retornam mais de uma coluna.
Erwin Brandstetter
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.