added explain and poryadok sql
This commit is contained in:
@@ -0,0 +1,151 @@
|
|||||||
|
--Задача 1 — Читаем план
|
||||||
|
Какой тип сканирования использует база?
|
||||||
|
--SeqScan - сканирование всей таблицы
|
||||||
|
|
||||||
|
Что означает строка `Index Cond: (id = 1)`?
|
||||||
|
-- У меня нет такой строки, есть "Filter: (id = 1)". Видимо это фильтрация по определенному значению, но в моем случае это должен быть индекс по столбцу id, но у меня индексы не проставлены, хорошо, что я хотя бы сейчас это уже понимаю.
|
||||||
|
|
||||||
|
Почему база использует индекс, а не читает всю таблицу?
|
||||||
|
--Потому что так быстрее и менее затратно.
|
||||||
|
----------
|
||||||
|
|
||||||
|
--Задача 2 — Seq Scan vs Index Scan
|
||||||
|
Почему в первом запросе используется Index Scan, а во втором может быть Seq Scan (если нет индекса на age)?
|
||||||
|
--Потому что Seq Scan сканирует таблицу полностью и ищет значения, а вот Index Scan нет необходимости сканировать всю таблицу, он отсекает по индексу.
|
||||||
|
----------
|
||||||
|
|
||||||
|
--Задача 3 — Замеряем время
|
||||||
|
1. Запиши **Execution Time**
|
||||||
|
--0.023 мс
|
||||||
|
|
||||||
|
2. Определи тип сканирования (Seq Scan или Index Scan)
|
||||||
|
--Seq Scan
|
||||||
|
|
||||||
|
3. Сколько строк вернул запрос?
|
||||||
|
--вернул 3 строки
|
||||||
|
----------
|
||||||
|
|
||||||
|
--Задача 4 — До и после индекса
|
||||||
|
Запиши Execution Time и тип сканирования.
|
||||||
|
--Seq Scan, 0.059 мс, после создания индекса запрос составил 0.026мс
|
||||||
|
|
||||||
|
Изменился ли тип сканирования?
|
||||||
|
-- Нет, не изменился, не могу понять почему.
|
||||||
|
|
||||||
|
Стал ли запрос быстрее? На сколько?
|
||||||
|
--Да, в 2 раза
|
||||||
|
|
||||||
|
Почему индекс помог?
|
||||||
|
--Потому что не пришлось искать по всей таблице.
|
||||||
|
|
||||||
|
----------
|
||||||
|
|
||||||
|
--Задача 5 — Анализируем JOIN
|
||||||
|
|
||||||
|
Какой тип JOIN использует база (Hash Join, Nested Loop, Merge Join)?
|
||||||
|
--Hash
|
||||||
|
|
||||||
|
С какой таблицы база начала чтение?
|
||||||
|
-- с users
|
||||||
|
|
||||||
|
Сколько времени занял весь запрос?
|
||||||
|
--execution time 0.032ms
|
||||||
|
----------
|
||||||
|
|
||||||
|
--Задача 6 — Почему медленно?
|
||||||
|
1. Выполни запрос и посмотри на план
|
||||||
|
--посмотрел
|
||||||
|
|
||||||
|
2. Использует ли база индекс?
|
||||||
|
--нет, используется seq scan
|
||||||
|
|
||||||
|
3. Почему НЕТ? (даже если индекс на email есть)
|
||||||
|
--Думаю, потому что также используется функция LOWER внутри WHERE
|
||||||
|
|
||||||
|
4. Как исправить запрос, чтобы индекс заработал?
|
||||||
|
CREATE INDEX idx_email ON users(email);
|
||||||
|
--я добавил индекс, но все равно EXPLAIN ANALYZE показывается Seq Scan. В чем проблема?
|
||||||
|
----------
|
||||||
|
|
||||||
|
--Задача 7 — Функции убивают индексы
|
||||||
|
Какой запрос работает быстрее?
|
||||||
|
--тот где нет дополнительной математики в условии
|
||||||
|
|
||||||
|
Почему первый запрос НЕ может использовать индекс на `age`?
|
||||||
|
--потому что появилась функция
|
||||||
|
|
||||||
|
Как правильно переписать запрос А?
|
||||||
|
-- убрать функцию из условия:
|
||||||
|
EXPLAIN ANALYZE
|
||||||
|
SELECT * FROM users WHERE age > 35;
|
||||||
|
|
||||||
|
----------
|
||||||
|
|
||||||
|
--Задача 8 — Диагностика медленного запроса
|
||||||
|
1. Почему функция `EXTRACT()` — плохая идея?
|
||||||
|
--потому что она мешает оптимизатору применить индекс.
|
||||||
|
|
||||||
|
2. Как переписать условие WHERE без функции?
|
||||||
|
--добавлением граничных условий что-то по типу created_at>='2026-01-01' AND created_at<'2027-01-01'
|
||||||
|
|
||||||
|
3. Будет ли использоваться индекс на `created_at` в текущем виде?
|
||||||
|
--нет, по описанным выше условиям
|
||||||
|
|
||||||
|
----------
|
||||||
|
|
||||||
|
--Задача 9 — SELECT * против конкретных колонок
|
||||||
|
Какой запрос работает быстрее?
|
||||||
|
-- запрос Б, там где выбираются не все столбцы
|
||||||
|
|
||||||
|
Почему второй может быть эффективнее (даже если тип сканирования одинаковый)?
|
||||||
|
--потому что объем обработанной информации меньше
|
||||||
|
|
||||||
|
В каких ситуациях разница будет особенно заметна?
|
||||||
|
--когда вся таблица изначально состоит из множества столбцов (более 10), а вт втором запросе мы берем только пару И когда количество строк очень большой.
|
||||||
|
|
||||||
|
----------
|
||||||
|
|
||||||
|
--Задача 10 — Оптимизируем JOIN с условием
|
||||||
|
1. Выполни запрос и посмотри на план
|
||||||
|
посмотрел
|
||||||
|
|
||||||
|
2. В каком порядке база читает таблицы?
|
||||||
|
БД читает таблицы в порядке, который является наименее затратным. Сначала идет отфильтровывание по условию,далее создается хэш таблица и хэш соединение, соединение по индексу.
|
||||||
|
|
||||||
|
3. Сколько строк проходит через каждый JOIN?
|
||||||
|
У меня здесь только 1 hash join - там 5 строк. Но сразу скажу, что одно из условий я удалил, так что у меня осталось только одно условие с category.
|
||||||
|
|
||||||
|
4. Можно ли оптимизировать? (Подсказка: подумай про индексы на `country` и `category`)
|
||||||
|
Я добавил индекс на category, поменялось количество hash join, стало 2.
|
||||||
|
Но сканирование не изменилось на индекс. Видимо, это связано с тем, что оптимизатору проще проходить по всей таблице, чем искать по индексу ввиду того, что таблица сама по себе не большая.
|
||||||
|
|
||||||
|
----------
|
||||||
|
|
||||||
|
--Задача 11 — Найди и исправь
|
||||||
|
Основной запрос
|
||||||
|
|
||||||
|
EXPLAIN ANALYZE
|
||||||
|
|
||||||
|
SELECT *
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN orders o ON u.id = o.user_id
|
||||||
|
WHERE LOWER(u.name) LIKE '%иван%'
|
||||||
|
AND EXTRACT(MONTH FROM o.order_date) = 1
|
||||||
|
AND u.age + 10 > 35;
|
||||||
|
|
||||||
|
Оптимизированный
|
||||||
|
|
||||||
|
EXPLAIN ANALYZE
|
||||||
|
|
||||||
|
SELECT u.name, o.order_date, u.age
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN orders o ON u.id = o.user_id
|
||||||
|
WHERE u.name LIKE '%иван%'
|
||||||
|
AND o.order_date >= '2024-01-01'
|
||||||
|
AND o.order_date < '2024-02-01'
|
||||||
|
AND u.age > 25;
|
||||||
|
|
||||||
|
По моим данным ожидаемое кол-во строк = 0, поэтому и запросы отрабатываются быстро = 0.03 мс.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
- **Задача 1** — Запрос с группировкой
|
||||||
|
|
||||||
|
SELECT country, COUNT(*) AS users_count
|
||||||
|
FROM users
|
||||||
|
WHERE is_active = true
|
||||||
|
GROUP BY country
|
||||||
|
HAVING COUNT(*) > 1
|
||||||
|
ORDER BY users_count DESC;
|
||||||
|
|
||||||
|
**❓ Вопросы:**
|
||||||
|
|
||||||
|
1. Когда применится `WHERE` — до или после `GROUP BY`?
|
||||||
|
До группировки, потому что это фильтрация строк
|
||||||
|
2. Когда применится `HAVING` — до или после `GROUP BY`?
|
||||||
|
После группировки, потому что это фильтрация групп
|
||||||
|
3. Можно ли использовать алиас `users_count` в `HAVING`?
|
||||||
|
Нельзя, тк HAVING применяется до SELECT
|
||||||
|
4. Можно ли использовать алиас `users_count` в `ORDER BY`?
|
||||||
|
Можно, тк ORDER BY применяется после SELECT
|
||||||
|
|
||||||
|
|
||||||
|
- **Задача 2** — JOIN с фильтрацией
|
||||||
|
|
||||||
|
SELECT u.name, o.order_date, o.status
|
||||||
|
FROM users u
|
||||||
|
JOIN orders o ON u.id = o.user_id
|
||||||
|
WHERE u.country = 'Россия' AND o.status = 'completed'
|
||||||
|
ORDER BY o.order_date DESC;
|
||||||
|
|
||||||
|
**✅ Задание:**
|
||||||
|
|
||||||
|
- Опиши порядок выполнения своими словами (как будто объясняешь другу-новичку).
|
||||||
|
На самом первом этапе идет формирование таблицы данных из фром и джойна, далее данные отфильтровываются в WHERE и выполняется вычисление выражение черезе SELECT, в конечном счете строки фильруются по order_date через ORDER BY
|
||||||
|
|
||||||
|
- **Задача 3** — Найди ошибку
|
||||||
|
|
||||||
|
Дан запрос:
|
||||||
|
|
||||||
|
SELECT country, AVG(age) AS avg_age
|
||||||
|
FROM users
|
||||||
|
GROUP BY country
|
||||||
|
HAVING country = 'Россия';
|
||||||
|
|
||||||
|
**❓ Вопросы:**
|
||||||
|
|
||||||
|
1. Запрос работает, но написан **неэффективно**. Почему?
|
||||||
|
Потому что фильтрация по "Россия" идет в самом конце запроса, когда можно было на первоначальном этапе сделать через WHERE, чтобы не читать всю таблицу. Плюс можно добавить индекс по столюцу country
|
||||||
|
2. Перепиши его правильно, используя WHERE.
|
||||||
|
SELECT
|
||||||
|
country,
|
||||||
|
AVG(age) AS avg_age
|
||||||
|
FROM users
|
||||||
|
WHERE country = 'RU'
|
||||||
|
GROUP BY country;
|
||||||
|
3. Объясни, почему твой вариант быстрее.
|
||||||
|
Объяснил в 1ом вопросе.
|
||||||
|
|
||||||
|
- **Задача 4** — Исправь запрос
|
||||||
|
|
||||||
|
SELECT category, AVG(price) AS avg_price
|
||||||
|
FROM products
|
||||||
|
GROUP BY category
|
||||||
|
HAVING category = 'Электроника' AND AVG(price) > 20000;
|
||||||
|
|
||||||
|
**✅ Задание:**
|
||||||
|
|
||||||
|
1. Перепиши запрос так, чтобы часть условий была в `WHERE`.
|
||||||
|
SELECT category, AVG(price) AS avg_price
|
||||||
|
FROM products
|
||||||
|
WHERE category = 'Электроника'
|
||||||
|
GROUP BY category
|
||||||
|
HAVING AVG(price) > 20000;
|
||||||
|
2. Объясни, почему это быстрее.
|
||||||
|
Потому что сразу происходит фильтрация строк и дальнейшая группировка и фильтрация уже не затрагивает ненужные строки (чем раньше фильтрация, тем лучше)
|
||||||
|
3. Нарисуй схему: сколько строк будет обработано в каждом случае.
|
||||||
|
Если не исполбзовать WHERE , то ан этапе группировке на входе будет 7 строк , которые далее отфильтуются.
|
||||||
|
А если сразу использовать WHERE, то на входе в группировку будет всего три строки
|
||||||
|
|
||||||
|
- **Задача 5** — Исправь все ошибки
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
country,
|
||||||
|
COUNT(*) AS users_count
|
||||||
|
FROM users
|
||||||
|
WHERE users_count > 1
|
||||||
|
GROUP BY country
|
||||||
|
ORDER BY country;
|
||||||
|
|
||||||
|
**✅ Задание:**
|
||||||
|
|
||||||
|
- Найди ошибку и исправь запрос.
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
country,
|
||||||
|
COUNT(*) AS users_count
|
||||||
|
FROM users
|
||||||
|
--WHERE users_count > 1
|
||||||
|
GROUP BY country
|
||||||
|
HAVING COUNT(*) > 1 --добавлена фильтрация по группам
|
||||||
|
ORDER BY country;
|
||||||
|
- **Задача 6** — Полный цикл выполнения
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
u.country,
|
||||||
|
COUNT(o.id) AS orders_count,
|
||||||
|
AVG(o.id) AS avg_order_id
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN orders o ON u.id = o.user_id
|
||||||
|
WHERE u.is_active = true
|
||||||
|
GROUP BY u.country
|
||||||
|
HAVING COUNT(o.id) > 1
|
||||||
|
ORDER BY orders_count DESC
|
||||||
|
LIMIT 3;
|
||||||
|
|
||||||
|
**✅ Задание:**
|
||||||
|
|
||||||
|
- Опиши ПОЛНЫЙ порядок выполнения (все 7 шагов) и объясни, что происходит на каждом.
|
||||||
|
1. Формирование таблицы данных из FROM и JOIN
|
||||||
|
2. Фильтрация строк WHERE
|
||||||
|
3. Группировка строк GROUP BY
|
||||||
|
4. Фильтрация групп HAVING
|
||||||
|
5. Вывод столбцов SELECT
|
||||||
|
6. Сортировка ORDER BY
|
||||||
|
7. Лимитирование выхода строк LIMIT
|
||||||
|
|
||||||
|
- **Задача 7** — Оптимизация
|
||||||
|
|
||||||
|
Сравни два запроса:
|
||||||
|
|
||||||
|
-- Запрос А
|
||||||
|
SELECT category, COUNT(*)
|
||||||
|
FROM products
|
||||||
|
GROUP BY category
|
||||||
|
HAVING category = 'Электроника';
|
||||||
|
|
||||||
|
-- Запрос Б
|
||||||
|
SELECT category, COUNT(*)
|
||||||
|
FROM products
|
||||||
|
WHERE category = 'Электроника'
|
||||||
|
GROUP BY category;
|
||||||
|
|
||||||
|
**❓ Вопросы:**
|
||||||
|
|
||||||
|
1. Какой запрос быстрее? Почему?
|
||||||
|
Запрос 2 быстрее потому что на этапе WHERE сразу отфильтровываются ненужные строки.
|
||||||
|
|
||||||
|
2. Нарисуй таблицу: на каком шаге сколько строк обрабатывается в каждом запросе.
|
||||||
|
На этапе FROM всего 7 строк. Далее если смотреть по запросу А они превращаются в 4 строки и на HAVING в 1.
|
||||||
|
Если смотреть на запрос Б, то после FROM остается 3 строки, содержащие данные только о категории 'Электроника', далее они просто группируются в одну.
|
||||||
|
|
||||||
|
3. Выполни оба с `EXPLAIN ANALYZE` и сравни `Execution Time.`
|
||||||
|
Запрос А - 0.04 мс
|
||||||
|
Запрос Б - 0.03 мс
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- **Задача 8** — Многоэтапная фильтрация
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
u.name,
|
||||||
|
COUNT(o.id) AS orders_count
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN orders o ON u.id = o.user_id
|
||||||
|
WHERE u.country = 'Россия'
|
||||||
|
AND u.age > 25
|
||||||
|
AND o.status = 'completed'
|
||||||
|
GROUP BY u.name
|
||||||
|
HAVING COUNT(o.id) >= 2
|
||||||
|
ORDER BY orders_count DESC;
|
||||||
|
|
||||||
|
**❓ Вопросы:**
|
||||||
|
|
||||||
|
1. Когда применяются условия `WHERE`?
|
||||||
|
Для фильтрации строк
|
||||||
|
2. Когда применяется условие `HAVING`?
|
||||||
|
Для фильтрации групп
|
||||||
|
3. В чём разница между фильтрацией по `o.status` в `WHERE` и по `COUNT(o.id)` в `HAVING`?
|
||||||
|
В WHERE фильтрация подразумевает выполненные заказы, а в HAVING подразумевает более 2х штук. Соответственно получается, что сначала мы фильтруем выполненные заказы, а потом людей у которых заказов более 2х штук.
|
||||||
|
|
||||||
|
|
||||||
|
- **Задача 9** — JOIN и ORDER BY
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
u.name,
|
||||||
|
p.name AS product_name,
|
||||||
|
p.price
|
||||||
|
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 = 'Россия'
|
||||||
|
ORDER BY p.price DESC
|
||||||
|
LIMIT 5;
|
||||||
|
|
||||||
|
**❓ Вопросы:**
|
||||||
|
|
||||||
|
1. На каком этапе применяется `ORDER BY`?
|
||||||
|
После FROM, JOIN, WHERE, SELECT и до LIMIT.
|
||||||
|
|
||||||
|
2. Почему `LIMIT` идёт после `ORDER BY`, а не до?
|
||||||
|
В таком случае нарушалась бы логика использования этих двух функций. Например, нужно вывести топ-3 (или топ-3 наименьшие) позиции из списка. И если лимит стоял бы до сортировки, то мы могли бы вывести совсем не топ значения. Данная последовательность помогает сначала сделать сортировка вверх или вниз и потом вывести нужные строки.
|
||||||
|
|
||||||
|
3. Что будет, если поменять их местами (технически невозможно, но подумай концептуально)?
|
||||||
|
В таком случае нарушалась бы логика использования этих двух функций. Например, нужно вывести топ-3 (или топ-3 наименьшие) позиции из списка. И если лимит стоял бы до сортировки, то мы могли бы вывести совсем не топ значения. Данная последовательность помогает сначала сделать сортировка вверх или вниз и потом вывести нужные строки.
|
||||||
|
|
||||||
|
- **Задача 10** — Напиши запрос с нуля
|
||||||
|
|
||||||
|
✅ Тебе нужно получить:
|
||||||
|
|
||||||
|
- Список категорий товаров
|
||||||
|
- Количество товаров в каждой категории
|
||||||
|
- Среднюю цену товаров в категории
|
||||||
|
- Только те категории, где средняя цена > 15000
|
||||||
|
- Только те категории, где товаров >= 2
|
||||||
|
- Отсортировать по средней цене (от большей к меньшей)
|
||||||
|
- Взять только топ-3
|
||||||
|
|
||||||
|
**✅ Задание:**
|
||||||
|
|
||||||
|
1. Напиши запрос
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
p.category AS category,
|
||||||
|
COUNT(*) AS count_product_in_category,
|
||||||
|
AVG(price) AS avg_price_product_in_category
|
||||||
|
FROM products p
|
||||||
|
GROUP BY p.category
|
||||||
|
HAVING AVG(price) > 15000 AND COUNT(*) >= 2
|
||||||
|
ORDER BY avg_price_product_in_category DESC
|
||||||
|
LIMIT 3;
|
||||||
|
|
||||||
|
2. Опиши порядок выполнения
|
||||||
|
1. Формируется таблица из FROM
|
||||||
|
2. Группировка GROUP BY (Так как строки фильтровать не нужно)
|
||||||
|
3. Фильтрация групп HAVING
|
||||||
|
4. Вывод значений SELECT
|
||||||
|
5. Сортировка ORDER BY
|
||||||
|
6. Лимитирование LIMIT
|
||||||
|
|
||||||
|
3. Объясни, почему использовал `WHERE` или `HAVING` для каждого условия
|
||||||
|
Использовал только HAVING потому что по условию нужно фильтровать именно группы, а не строки.
|
||||||
|
|
||||||
|
4. Выполни с `EXPLAIN ANALYZE` и посмотри план
|
||||||
|
1. чтение таблицы products
|
||||||
|
2. Группировка с фильтрацией групп (hash aggregate)
|
||||||
|
3. Сортировка Sort key
|
||||||
|
4. LIMIT
|
||||||
Reference in New Issue
Block a user