CodeGym /Cursos /SQL SELF /Exemplos de Consultas Complexas com JSONB

Exemplos de Consultas Complexas com JSONB

SQL SELF
Nível 34 , Lição 2
Disponível

Nas aulas anteriores, a gente viu o básico do JSONB: como criar, alterar e buscar dados. Agora chegou a hora do desafio de verdade — consultas complexas que mostram todo o poder desse tipo de dado.

Imagina uma loja online com um catálogo de produtos. Cada produto tem informações básicas (nome, ID), mas as características podem ser totalmente diferentes: notebook tem RAM e CPU, roupa tem tamanho e material, livro tem autor e gênero. Guardar tudo isso em tabelas separadas? Meio chato. Em JSONB? Perfeito! Mas como achar todos os produtos de uma marca específica, ordenar pelo preço ou calcular estatísticas por categoria? Como trabalhar com dados que não estão em colunas normais, mas sim escondidos dentro de uma estrutura JSON?

Hoje a gente vai ver cenários reais: desde um filtro simples até consultas cabulosas com agrupamento e agregação. Você vai ver como o JSONB transforma o PostgreSQL numa ferramenta super flexível pra qualquer tipo de dado.

Filtrando Dados no JSONB

Filtrar dados é tipo peneirar chá: você só deixa passar o que interessa e tira o resto. Com JSONB fica ainda mais massa, porque dá pra filtrar não só por colunas normais, mas também por dados que estão lá no fundo da estrutura JSON.

Operadores pra filtrar JSONB:

  • @> — "JSONB-contem". Checa se o objeto JSONB tem o subconjunto informado.
  • ? — "Chave existe". Vê se a chave informada existe no objeto JSONB.
  • ?| — "Qualquer chave existe". Vê se pelo menos uma das chaves informadas existe.
  • ?& — "Todas as chaves existem". Checa se todas as chaves informadas existem.

Exemplo: filtrando por chave e valor. Imagina que temos uma tabela products com uma coluna details que guarda as infos dos produtos em JSONB:

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name TEXT,
    details JSONB
);

Exemplo de dados:

INSERT INTO products (name, details) VALUES
('Laptop', '{"brand": "Dell", "price": 1200, "specs": {"ram": "16GB", "cpu": "i7"}}'),
('Smartphone', '{"brand": "Apple", "price": 800, "specs": {"ram": "4GB", "cpu": "A15"}}'),
('Tablet', '{"brand": "Samsung", "price": 500, "specs": {"ram": "8GB", "cpu": "Exynos"}}');

Resultado:

id name details
1 Laptop {"brand": "Dell", "price": 1200, "specs": {"ram": "16GB", "cpu": "i7"}}
2 Smartphone {"brand": "Apple", "price": 800, "specs": {"ram": "4GB", "cpu": "A15"}}
3 Tablet {"brand": "Samsung", "price": 500, "specs": {"ram": "8GB", "cpu": "Exynos"}}

Pra achar todos os produtos com brand "Apple":

SELECT *
FROM products 
WHERE details @> '{"brand": "Apple"}';

Resultado:

id name details
2 Smartphone {"brand": "Apple", "price": 800, "specs": {"ram": "4GB", "cpu": "A15"}}

Se você quiser achar todos os produtos que têm a chave specs, usa o operador ?:

SELECT *
FROM products 
WHERE details ? 'specs';

Resultado:

id name details
1 Laptop {"brand": "Dell", "price": 1200, "specs": {"ram": "16GB", "cpu": "i7"}}
2 Smartphone {"brand": "Apple", "price": 800, "specs": {"ram": "4GB", "cpu": "A15"}}
3 Tablet {"brand": "Samsung", "price": 500, "specs": {"ram": "8GB", "cpu": "Exynos"}}

Todas as linhas têm o campo details e a chave specs.

Ordenando Dados no JSONB

Às vezes você quer ordenar os dados não por colunas normais, mas por valores que estão dentro do JSONB. Pra isso, dá pra usar os operadores ->> (pega o valor como texto) e CAST pra transformar o texto no tipo que você quiser.

Exemplo: vamos ordenar os produtos pelo preço:

SELECT *
FROM products 
ORDER BY (details->>'price')::INTEGER;

Resultado:

id name details
3 Tablet {"brand": "Samsung", "price": 500, "specs": {"ram": "8GB", "cpu": "Exynos"}}
2 Smartphone {"brand": "Apple", "price": 800, "specs": {"ram": "4GB", "cpu": "A15"}}
1 Laptop {"brand": "Dell", "price": 1200, "specs": {"ram": "16GB", "cpu": "i7"}}

Agrupando Dados no JSONB

Agrupar serve pra agregar dados e mostrar estatísticas. Por exemplo, você pode ver quantos produtos tem de cada marca.

Exemplo: vamos contar quantos produtos tem pra cada marca:

SELECT
    details->>'brand' AS brand,
    COUNT(*) AS product_count
FROM products
GROUP BY details->>'brand';

Resultado:

brand product_count
Dell 1
Apple 1
Samsung 1

Exemplos Práticos

Filtrando e agrupando. Vamos contar quantos produtos custam mais de 600 pra cada marca:

SELECT
    details->>'brand' AS brand,
    COUNT(*) AS product_count
FROM products
WHERE (details->>'price')::INTEGER > 600
GROUP BY details->>'brand';

Resultado:

brand product_count
Dell 1
Apple 1

Ordenando depois de agrupar. Agora vamos ordenar as marcas pela quantidade de produtos:

SELECT
    details->>'brand' AS brand,
    COUNT(*) AS product_count
FROM products
GROUP BY details->>'brand'
ORDER BY product_count DESC;

Consulta Complexa: filtrando, ordenando, agrupando

Imagina que você quer achar as marcas que têm produtos acima de 600 e pegar o produto mais barato de cada marca. Olha só como faz:

WITH filtered_products AS (
    SELECT *
    FROM products
    WHERE (details->>'price')::INTEGER > 600
)
SELECT
    details->>'brand' AS brand,
    MIN((details->>'price')::INTEGER) AS min_price
FROM filtered_products
GROUP BY details->>'brand'
ORDER BY min_price;

Resultado:

brand min_price
Apple 800
Dell 1200

Erros Comuns e Dicas

Erro: Usando operadores errado. Não confunde os operadores -> e ->>: o primeiro retorna objeto, o segundo retorna valor como texto.

Erro: Problemas de performance. Se você faz muita consulta pesada, cria um índice GIN na coluna JSONB.

Erro: Problemas de tipo. Os valores do JSONB são strings, então lembra de usar CAST.

Exemplo de criação de índice:

CREATE INDEX idx_products_details ON products USING GIN (details);

Agora, filtros tipo details @> '{"brand": "Apple"}' vão rodar bem mais rápido.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION