193 lines
7.5 KiB
SQL
193 lines
7.5 KiB
SQL
- **Задача 1** — Когда какой метод?
|
||
|
||
✅ Заполни таблицу:
|
||
|
||
| **Ситуация** | **Какой JOIN выберет база?** | **Почему?** |
|
||
| --- | --- | --- |
|
||
| 10 пользователей, 1 млн заказов, есть индекс на orders.user_id | Nested Loop | Потому что есть индекс по одному полю и одна таблица мееьше другой сильно |
|
||
| 100 000 пользователей, 100 000 заказов, нет индексов | Hash Join | Нет ни индексов, ни сортировки |
|
||
| Обе таблицы отсортированы по ключу, есть индексы | Merge Join | Потому что есть и индексы и сортировка |
|
||
| Маленькая таблица (5 строк) соединяется с большой (1 млн строк), нет индекса | Hash Join | Потому что нет индекса |
|
||
|
||
|
||
- **Задача 2** — Размер таблиц имеет значение
|
||
|
||
✅ Создай тестовые данные:
|
||
|
||
|
||
-- Маленькая таблица (10 строк)
|
||
CREATE TABLE small_users AS
|
||
SELECT generate_series(1, 10) AS id, 'User ' || generate_series(1, 10) AS name;
|
||
|
||
-- Большая таблица (100 000 строк)
|
||
CREATE TABLE big_orders AS
|
||
SELECT
|
||
generate_series(1, 100000) AS id,
|
||
(random() * 9 + 1)::int AS user_id,
|
||
CURRENT_DATE - (random() * 365)::int AS order_date;
|
||
|
||
|
||
✅ Теперь выполни:
|
||
|
||
|
||
-- Вариант 1: Без индекса
|
||
EXPLAIN ANALYZE
|
||
SELECT u.name, COUNT(o.id)
|
||
FROM small_users u
|
||
JOIN big_orders o ON u.id = o.user_id
|
||
GROUP BY u.name;
|
||
|
||
-- Вариант 2: С индексом
|
||
CREATE INDEX idx_big_orders_user_id ON big_orders(user_id);
|
||
|
||
EXPLAIN ANALYZE
|
||
SELECT u.name, COUNT(o.id)
|
||
FROM small_users u
|
||
JOIN big_orders o ON u.id = o.user_id
|
||
GROUP BY u.name;
|
||
|
||
**❓ Вопросы:**
|
||
|
||
1. Какой метод `JOIN` используется в каждом случае?
|
||
В обоих случаях у меня показало Merge Join, просто там где индекс - там Scan Index.
|
||
2. Почему с индексом база может выбрать `Nested Loop`?
|
||
Потому что будет маленькая таблица, иногда проще заджойнить большую и маленькую Nested Loop.
|
||
3. Насколько быстрее стало с индексом?
|
||
В 2,5 раза.
|
||
|
||
- **Задача 3** — Влияние сортировки
|
||
|
||
✅ Выполни три варианта:
|
||
|
||
-- Вариант 1: Без ORDER BY
|
||
EXPLAIN ANALYZE
|
||
SELECT u.name, o.order_date
|
||
FROM users u
|
||
JOIN orders o ON u.id = o.user_id;
|
||
|
||
-- Вариант 2: С ORDER BY по ключу соединения
|
||
EXPLAIN ANALYZE
|
||
SELECT u.name, o.order_date
|
||
FROM users u
|
||
JOIN orders o ON u.id = o.user_id
|
||
ORDER BY u.id, o.user_id;
|
||
|
||
-- Вариант 3: С ORDER BY по другому полю
|
||
EXPLAIN ANALYZE
|
||
SELECT u.name, o.order_date
|
||
FROM users u
|
||
JOIN orders o ON u.id = o.user_id
|
||
ORDER BY o.order_date;
|
||
|
||
**❓Вопросы:**
|
||
|
||
1. Изменился ли тип `JOIN` во втором варианте?
|
||
В первом Hash, во втором - Merge.
|
||
2. Появился ли `Merge Join`?
|
||
Да.
|
||
3. Почему `ORDER BY` может повлиять на выбор `JOIN`?
|
||
Потому что появляется метод при соединении (при условии наличия order by), упрощающий join.
|
||
|
||
- **Задача 4** — Найди узкое место
|
||
|
||
✅ Дан медленный запрос:
|
||
|
||
SELECT
|
||
u.name,
|
||
p.name AS product_name,
|
||
oi.quantity,
|
||
o.order_date
|
||
FROM users u
|
||
JOIN orders o ON u.id = o.user_id
|
||
JOIN order_items oi ON o.id = oi.order_id
|
||
JOIN products p ON oi.product_id = p.id
|
||
WHERE u.country = 'Россия';
|
||
|
||
**✅ Задание:**
|
||
|
||
1. Выполни с `EXPLAIN ANALYZE`
|
||
2. Посмотри, какие типы `JOIN` используются
|
||
Nested Loop and Hash Join.
|
||
3. Есть ли `Seq Scan`? Если да — где?
|
||
Да, в трех местах. При чтении таблицы users, orders и products.
|
||
4. Предложи, какие индексы нужно создать
|
||
Можно создать 3 индекса
|
||
5. Создай индексы и проверь, изменились ли типы `JOIN`
|
||
Изменились, но не сильно, в основном все Hash, один внешний Nested
|
||
|
||
- **Задача 5** — Многоступенчатый JOIN
|
||
|
||
EXPLAIN ANALYZE
|
||
SELECT
|
||
u.name,
|
||
COUNT(DISTINCT o.id) AS orders_count,
|
||
COUNT(DISTINCT oi.product_id) AS products_count
|
||
FROM users u
|
||
LEFT JOIN orders o ON u.id = o.user_id
|
||
LEFT JOIN order_items oi ON o.id = oi.order_id
|
||
WHERE u.country = 'Россия'
|
||
GROUP BY u.name;
|
||
|
||
**✅ Задание:**
|
||
|
||
1. Посмотри на план выполнения
|
||
2. Какие методы `JOIN` используются на каждом уровне?
|
||
Hash right join 2 раза
|
||
3. Почему для первого `JOIN` один метод, а для второго — другой?
|
||
У меня оба одинаковые, вероятнее всего из-за неверных индексов или чего-то другого.
|
||
4. Сколько строк проходит через каждый `JOIN`?
|
||
По одной
|
||
|
||
- **Задача 6** — `LEFT JOIN` vs `INNER JOIN`
|
||
|
||
✅ Сравни два запроса:
|
||
|
||
-- Вариант A: INNER JOIN
|
||
EXPLAIN ANALYZE
|
||
SELECT u.name, o.order_date
|
||
FROM users u
|
||
INNER JOIN orders o ON u.id = o.user_id;
|
||
|
||
-- Вариант B: LEFT JOIN
|
||
EXPLAIN ANALYZE
|
||
SELECT u.name, o.order_date
|
||
FROM users u
|
||
LEFT JOIN orders o ON u.id = o.user_id;
|
||
|
||
**❓ Вопросы:**
|
||
|
||
1. Одинаковый ли тип физического JOIN используется?
|
||
Hash and Hash Right
|
||
2. Есть ли разница в Execution Time?
|
||
Не сильно , скачет то в одну, то в другую сторону
|
||
3. Почему LEFT JOIN может быть медленнее?
|
||
|
||
|
||
- **Задача 7** — Порядок таблиц в JOIN
|
||
|
||
✅ Сравни:
|
||
|
||
-- Вариант 1: users JOIN orders
|
||
EXPLAIN ANALYZE
|
||
SELECT u.name, o.order_date
|
||
FROM users u
|
||
JOIN orders o ON u.id = o.user_id;
|
||
|
||
-- Вариант 2: orders JOIN users
|
||
EXPLAIN ANALYZE
|
||
SELECT u.name, o.order_date
|
||
FROM orders o
|
||
JOIN users u ON o.user_id = u.id;
|
||
|
||
**❓ Вопросы:**
|
||
|
||
1. Изменился ли тип `JOIN`?
|
||
нет
|
||
2. Изменился ли порядок чтения таблиц в плане?
|
||
нет
|
||
3. Одинаковое ли `Execution Time`?
|
||
Да
|
||
4. Почему оптимизатор может выбрать разный порядок?
|
||
потому чтт ему будет так проще соединять таблицы и читать данные
|
||
|