diff --git a/dmedvedev/10.4.Explain.sql b/dmedvedev/10.4.Explain.sql new file mode 100644 index 0000000..a240058 --- /dev/null +++ b/dmedvedev/10.4.Explain.sql @@ -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 мс. + + + diff --git a/dmedvedev/10.5.Porydok.sql b/dmedvedev/10.5.Porydok.sql new file mode 100644 index 0000000..bb85eeb --- /dev/null +++ b/dmedvedev/10.5.Porydok.sql @@ -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 \ No newline at end of file