JavaRush /Курсы /SQL SELF /Примеры сложных запросов с JSONB

Примеры сложных запросов с JSONB

SQL SELF
34 уровень , 2 лекция
Открыта

В предыдущих лекциях мы изучили основы JSONB: как создавать, изменять и извлекать данные. Теперь пришло время для настоящих испытаний — сложных запросов, которые покажут всю мощь этого типа данных.

Представьте интернет-магазин с каталогом товаров. У каждого товара есть базовая информация (название, ID), но характеристики могут кардинально отличаться: у ноутбука есть RAM и CPU, у одежды — размеры и материалы, у книг — авторы и жанры. Хранить всё это в отдельных таблицах? Неудобно. В JSONB? Идеально! Но как найти все товары определенного бренда, отсортировать их по цене или посчитать статистику по категориям? Как работать с данными, которые находятся не в обычных столбцах, а спрятаны внутри JSON-структуры?

Сегодня мы разберем реальные сценарии: от простой фильтрации до комплексных запросов с группировкой и агрегацией. Вы увидите, как JSONB превращает PostgreSQL в гибкий инструмент для работы с любыми данными.

Фильтрация данных в JSONB

Фильтрация данных — это как ситечко для чая: вы оставляете только то, что нужно, и убираете лишнее. С JSONB все становится еще интереснее, так как мы можем фильтровать не только по обычным столбцам, но и по данным, спрятанным глубоко внутри JSON-структуры.

Операторы для фильтрации JSONB:

  • @> — "JSONB-содержит". Проверяет, содержит ли JSONB-объект указанный подмножество.
  • ? — "Ключ присутствует". Проверяет, есть ли указанный ключ в JSONB-объекте.
  • ?| — "Любой из ключей присутствует". Проверяет, есть ли хотя бы один из указанных ключей.
  • ?& — "Все ключи присутствуют". Проверяет, что все указанные ключи присутствуют.

Пример: фильтрация по ключу и его значению. Допустим, у нас есть таблица products с колонкой details, которая хранит JSONB-информацию о товарах:

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

Пример данных:

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"}}');

Результат:

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"}}

Чтобы найти все продукты с brand "Apple":

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

Результат:

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

Если вы хотите найти все продукты, у которых есть ключ specs, используйте оператор ?:

SELECT *
FROM products 
WHERE details ? 'specs';

Результат:

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"}}

Все строки содержат поле details и ключ specs.

Сортировка данных в JSONB

Иногда вам нужно отсортировать данные не по обычным столбцам, а по значениям, которые находятся внутри JSONB. Для этого вы можете использовать операторы ->> (извлечение значения в виде текста) и CAST для преобразования текстового значения в нужный тип.

Пример: отсортируем продукты по цене:

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

Результат:

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"}}

Группировка данных в JSONB

Группировка позволяет агрегировать данные и выводить статистику. Например, вы можете узнать, сколько продуктов приходится на каждый бренд.

Пример: посчитаем количество продуктов для каждого бренда:

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

Результат:

brand product_count
Dell 1
Apple 1
Samsung 1

Практические примеры

Фильтрация и группировка. Посчитаем, сколько продуктов дороже 600 для каждого бренда:

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

Результат:

brand product_count
Dell 1
Apple 1

Сортировка после группировки. Теперь отсортируем бренды по количеству продуктов:

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

Комплексный запрос: фильтрация, сортировка, группировка

Представим, что вы хотите найти бренды, у которых есть продукты дороже 600 и выбрать один самый дешевый продукт для каждого бренда. Вот как это сделать:

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;

Результат:

brand min_price
Apple 800
Dell 1200

Типичные ошибки и советы

Ошибка: Неправильное использование операторов. Не путайте операторы -> и ->>: первый возвращает объект, второй — текстовое значение.

Ошибка: Проблемы с производительностью. Если вы часто выполняете сложные запросы, создайте индекс GIN на JSONB-колонке.

Ошибка: Проблемы с типами. Значения из JSONB — это строки, так что не забывайте использовать CAST.

Пример создания индекса:

CREATE INDEX idx_products_details ON products USING GIN (details);

Теперь фильтрация вроде details @> '{"brand": "Apple"}' будет работать гораздо быстрее.

2
Задача
SQL SELF, 34 уровень, 2 лекция
Недоступна
Фильтрация продуктов по ключу в JSONB
Фильтрация продуктов по ключу в JSONB
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ