product_table
+----+----------------------+-------+-------------+
| id | Название | Цена | category_id |
+----+----------------------+-------+-------------+
| 1 | iPhone 13 | 1000 | 2 |
| 2 | Galaxy S21 | 800 | 2 |
| 3 | Pixel 6 | 700 | 2 |
| 4 | MacBook Air | 1000 | 4 |
| 5 | Surface Pro 8 | 1200 | 4 |
| 6 | iPad Pro | 800 | 3 |
| 7 | iPad Mini | 400 | 9 |
| 8 | Smart TV | 800 | 1 |
| 9 | Home Theater System | 600 | 1 |
| 10 | Galaxy S21 Ultra | 1400 | 2 |
| 11 | iPhone 13 Pro Max | 1300 | 2 |
| 12 | Pixel 6 Pro | 1000 | 2 |
+----+----------------------+-------+-------------+
category_table
+----+-------------+
| id | Название |
+----+-------------+
| 1 | Электроника |
| 2 | Телефоны |
| 3 | Планшеты |
| 4 | Ноутбуки |
| 5 | Бытовая техника |
| 6 | Одежда |
| 7 | Спорттовары |
| 8 | Игрушки |
| 9 | Аксессуары |
+----+-------------+
order_table
+----+------------+-------------+------------+
| id | product_id | Количество | Дата |
+----+------------+-------------+------------+
| 1 | 1 | 2 | 2022-03-28 |
| 2 | 2 | 1 | 2022-03-28 |
| 3 | 3 | 3 | 2022-03-29 |
| 4 | 4 | 1 | 2022-03-30 |
| 5 | 5 | 2 | 2022-03-31 |
| 6 | 6 | 3 | 2022-03-31 |
| 7 | 7 | 2 | 2022-03-31 |
| 8 | 8 | 1 | 2022-03-31 |
| 9 | 9 | 1 | 2022-03-31 |
+--------------------------------------------+
Пример. Чтобы получить все продукты дороже 1000 $, применяем WHERE для их фильтрации по цене, в результаты включаются только продукты дороже 1000 $:
SELECT *
FROM product_table
WHERE price > 1000;
+----+------------+----------+-------------+---------------------+
| id | product_id | Количество | Дата | Название |
+----+------------+----------+-------------+---------------------+
| 1 | 1 | 2 | 2022-03-28 | iPhone 13 |
| 2 | 2 | 1 | 2022-03-28 | Galaxy S21 |
| 3 | 3 | 3 | 2022-03-29 | Pixel 6 |
| 4 | 4 | 1 | 2022-03-30 | MacBook Air |
| 5 | 5 | 2 | 2022-03-31 | Surface Pro 8 |
| 6 | 6 | 3 | 2022-03-31 | iPad Pro |
| 7 | 7 | 2 | 2022-03-31 | iPad Mini |
| 8 | 8 | 1 | 2022-03-31 | Smart TV |
| 9 | 9 | 1 | 2022-03-31 | Home Theater System |
+----+------------+----------+-------------+---------------------+
Пример. Чтобы получить список всех продуктов, заказанных хотя бы раз, внутри основного запроса пишем подзапрос с критериями для фильтрации данных. Результат подзапроса — все ID заказанных хотя бы раз продуктов — включается в условие фильтра основного запроса. В итоге получаем все данные о продуктах, соответствующих критериям подзапроса, со списком всех заказанных хотя бы раз товаров:
SELECT *
FROM product_table
WHERE id IN (
SELECT DISTINCT product_id
FROM order_table
);
+----+---------------------+-------+-------------+
| id | Название | Цена | category_id |
+----+---------------------+-------+-------------+
| 1 | iPhone 13 | 999 | 2 |
| 2 | Galaxy S21 | 1099 | 2 |
| 3 | Pixel 6 | 899 | 2 |
| 4 | MacBook Air | 1199 | 3 |
| 5 | Surface Pro 8 | 1200 | 4 |
| 6 | iPad Pro | 799 | 4 |
| 7 | iPad Mini | 499 | 4 |
| 8 | Smart TV | 899 | 1 |
| 9 | Home Theater System | 799 | 1 |
+----+---------------------+-------+-------------+
Пример. Чтобы рассчитать нарастающий итог по дате заказа каждого продукта в одном запросе, применяем оконную функцию к подмножеству, называемому окном, в которое включаются все строки конкретного продукта, связанные друг с другом по заданным критериям:
SELECT name, quantity, SUM(quantity) OVER (
PARTITION BY product_id
ORDER BY date
) AS running_total
FROM order_table
INNER JOIN product_table ON order_table.product_id = product_table.id
ORDER BY product_id, date;
+--------------+----------+---------------+
| Название | Количество | running_total |
+--------------+----------+---------------+
| iPhone 13 | 2 | 2 |
| iPhone 13 | 1 | 3 |
| Galaxy S21 | 1 | 1 |
| Pixel 6 | 3 | 3 |
| MacBook Air | 1 | 1 |
| Surface Pro 8| 2 | 2 |
| iPad Pro | 3 | 3 |
| iPad Mini | 2 | 2 |
| Smart TV | 1 | 1 |
| Home Theater | 1 | 1 |
+--------------+----------+---------------+
Пример. Чтобы найти общий доход по каждой категории, обобщенным табличным выражением высчитываем его с помощью подзапроса по каждому продукту и объединяем с таблицей категорий. Так получим общий доход по каждой категории продуктов в одном запросе:
WITH product_revenue AS (
SELECT product_id, SUM(quantity * price) AS revenue
FROM order_table
INNER JOIN product_table ON order_table.product_id = product_table.id
GROUP BY product_id
)
SELECT category_table.name, SUM(product_revenue.revenue) AS total_revenue
FROM category_table
INNER JOIN product_table ON category_table.id = product_table.category_id
INNER JOIN product_revenue ON product_table.id = product_revenue.product_id
GROUP BY category_table.name;
+------------+---------------+
| Название | total_revenue |
+------------+---------------+
| Электроника| 6094 |
| Home | 1598 |
+------------+---------------+
Пример. Чтобы рассчитать общий доход по каждому проданному за последний месяц продукту, создаем материализованное представление, в котором это значение вычисляется и сохраняется в виде физической таблицы. Больше не нужно каждый раз вычислять его, просто выполняем запрос в материализованное представление:
CREATE MATERIALIZED VIEW product_monthly_revenue AS
SELECT product_id, SUM(quantity * price) AS monthly_revenue
FROM order_table
INNER JOIN product_table ON order_table.product_id = product_table.id
WHERE order_date BETWEEN CURRENT_DATE - INTERVAL '1 month' AND CURRENT_DATE
GROUP BY product_id;
SELECT product_table.name, product_monthly_revenue.monthly_revenue
FROM product_table
INNER JOIN product_monthly_revenue
ON product_table.id = product_monthly_revenue.product_id;
+----------------------+----------------+
| Название | monthly_revenue|
+----------------------+----------------+
| Ноутбук | 1200 |
| Смартфон | 800 |
| Наушники | 300 |
+----------------------+----------------+
Пример. Найдем в большой — на миллионы строк — таблице заказов orders все заказы, размещенные конкретным покупателем:
SELECT *
FROM orders
WHERE customer_id = 12345;
CREATE INDEX customer_id_idx ON orders (customer_id);