Files
homework/dmedvedev/2.2.theory_bd_partitions.sql
Dmitry Medvedev 4f56e71ef8 fix mistakes
2026-08-01 22:07:18 +05:00

171 lines
10 KiB
SQL
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
- Задание 1. Какой вид партиционирования выбрать
**Ответ текстом.**
1. Таблица `orders(id, user_id, order_date, status)` — очень большая (10 лет истории).
- Какой вид партиционирования (RANGE / LIST / HASH) ты выберешь и по какому полю?
ДУМАЮ МОЖНО СДЕЛАТЬ 2 ПАРТИЦИИ, ПЕРВАЯ RANGE ПО ПОЛЯМ USER_ID И ORDER_DATE - RANGE. И ВТОРАЯ ПАРТИЦИЯ ПО USER_ID И STATUS - LIST
- Какие типы запросов выиграют от этого выбора? Приведи 3 примера запросов.
2. Таблица `users(id, name, age, email, country, is_active, created_at)` — много пользователей по разным странам.
- Выбери вид партиционирования (можно «не партиционировать») и обоснуй.
МОЖНО ПО АЙДИ И СТРАНЕ - LIST. ПО АЙДИ И CREATE_AT - RANGE. COUNTRY И IS_ACTIVE - ЧТОБЫ ПОНИМАТЬ В КАКОЙ СТРАНЕ БОЛЬШЕ АКТИВНЫХ ПОЛЬЗОВАТЕЛЕЙ - МОЖЕТ HASH?
- Если ты выбрал LIST по `country`, какие плюсы и минусы у такого решения?
Огромное количество стран - минус.
3. Таблица логов `user_logs(id, user_id, created_at, event_type, payload)` — миллионы записей в день.
- Какой вид партиционирования по какому полю логичен (RANGE / HASH) и почему?
USER_ID и EVENT_TYPE по LIST или по HASH.
- Задание 2. Размер партиций
**Ответ текстом.**
1. Для `orders` с партиционированием по `order_date` придумай три варианта:
- партиции по годам;
- по месяцам;
- по дням.
2. Для каждого варианта напиши:
- примерно сколько будет партиций за 10 лет;
по годам - 10 * 1 = 10, по месяцам - 10 * 12 = 120, по дням 366 * 2 + 365 * 8 = 3652.
- плюсы и минусы (удаление старых данных, поддержка, скорость запросов).
3. Выбери один вариант и обоснуй: почему он оптимален для большого онлайн‑магазина.
Для онлайн магазина лучше подойдет по месяцам, чтобы понимать сезонность товаров.
- Задание 3. RANGE‑партиции по дате заказов
**Уточнение**: партиционировать уже существующую таблицу муторно, проще создать новую
**Сделать в PostgreSQL:**
- создать партиционированную таблицу `orders`, разбитую методом RANGE по полю `order_date`;
CREATE TABLE orderz (
id INT GENERATED ALWAYS AS IDENTITY,
user_id INT NOT NULL,
order_date DATE NOT NULL,
status TEXT NOT NULL,
PRIMARY KEY (id, order_date)
) PARTITION BY RANGE(order_date);
- создать несколько партиций (например, по годам или другим диапазонам дат), чтобы вставка строк с разными датами автоматически попадала в нужную партицию;
CREATE TABLE orderz_2023 PARTITION OF orderz
FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');
CREATE TABLE orderz_2024 PARTITION OF orderz
FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
CREATE TABLE orderz_2025 PARTITION OF orderz
FOR VALUES FROM ('2025-01-01') TO ('2026-01-01');
CREATE TABLE orderz_before_2023 PARTITION OF orderz
FOR VALUES FROM ('2000-01-01') TO ('2023-01-01');
- вставить тестовые данные с разными датами;
INSERT INTO orderz (user_id, order_date, status)
VALUES
(1, '2023-04-06', 'paid'),
(2, '2024-07-17', 'decline'),
(3, '2007-11-27', 'error');
- Написать несколько запросов выборки (за один год, за несколько лет, за весь период)
SELECT *
FROM orderz o
WHERE order_date BETWEEN ('2023-01-01') AND ('2023-12-31');
SELECT *
FROM orderz o
WHERE order_date BETWEEN ('2022-01-01') AND ('2024-12-31');
SELECT *
FROM orderz o;
- Объяснить, в каких случаях БД будет читать только часть партиций, а в каких — все.
Если мы выставляем определенное условие соответствующее одной партиции, то чтение будет по этой же партиции, если выставляем по нескольким, то чтение по нескольким.
- Задание 4. LIST‑партиции по стране пользователей
**Сделать в PostgreSQL:**
- создать партиционированную таблицу `users`, разделённую методом LIST по полю `country`;
CREATE TABLE userz (
id INT PRIMARY KEY,
name TEXT NOT NULL,
age INT,
email TEXT NOT NULL,
country TEXT NOT NULL,
is_active BOOLEAN NOT NULL,
created_at TIMESTAMP NOT NULL
) PARTITION BY LIST (country);
- определить несколько партиций для разных стран или групп стран и вставить тестовые данные так, чтобы строки автоматически распределялись по этим партициям;
CREATE TABLE userz_ru PARTITION OF userz
FOR VALUES IN ('RU');
CREATE TABLE userz_us PARTITION OF userz
FOR VALUES IN ('US');
CREATE TABLE userz_other PARTITION OF userz
FOR VALUES IN (DEFAULT);
- написать запросы выборки:
- по конкретной стране (например, только пользователи из одной страны);
SELECT *
FROM userz u
WHERE country = 'RU';
- по условию, не связанному с `country` (например, все активные пользователи);
SELECT *
FROM userz u
WHERE status = 'is_active';
- Объясни, для какого запроса PostgreSQL может обращаться только к одной/нескольким партициям, а для какого придётся просматривать все партиции.
Первый запрос обращается именно к одной партиции, а второй уже ко всем, потому что вто второй нет условия по партиции 'RU'.
- Задание 5. HASH‑партиции по user_id в логах
**Сделать в PostgreSQL:**
- создать таблицу `user_logs`, партиционированную методом HASH по полю `user_id`, с несколькими партициями;
CREATE TABLE user_logz (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
created_at TIMESTAMP NOT NULL,
event_type TEXT NOT NULL,
payload JSONB
) PARTITION BY HASH (user_id);
CREATE TABLE user_logz_p0 PARTITION OF user_logz
FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE user_logz_p1 PARTITION OF user_logz
FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE user_logz_p2 PARTITION OF user_logz
FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE user_logz_p3 PARTITION OF user_logz
FOR VALUES WITH (MODULUS 4, REMAINDER 3);
- заполнить таблицу тестовыми строками с разными значениями `user_id`;
INSERT INTO user_logz (user_id, created_at, event_type, payload)
VALUES (10, now(), 'login', '{"ip": "127.0.0.1"}');
SELECT * FROM user_logz
WHERE created_at >= now() - interval '1 day';
- написать запрос:
- выборки всех логов по конкретному `user_id`;
SELECT * FROM user_logz WHERE user_id = 10;
- выборки всех логов за период по полю `created_at` (без фильтра по `user_id`);
SELECT * FROM user_logz
WHERE created_at >= now() - interval '1 day';
- Объясни какой запрос позволит серверу обратиться только к одной партиции, а какой заставит сканировать все партиции и почему.
Первый запрос обращается к одной партиции потому что есть условие по партиционному полю, второй именно ко всем таблицам, потому что нет условия по партиции.