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