diff --git a/mnoskov/postgresql/9. Оптимизация/5. Порядок выполнения команд/DQL_command_execution_order_practice.sql b/mnoskov/postgresql/9. Оптимизация/5. Порядок выполнения команд/DQL_command_execution_order_practice.sql new file mode 100644 index 0000000..55377f9 --- /dev/null +++ b/mnoskov/postgresql/9. Оптимизация/5. Порядок выполнения команд/DQL_command_execution_order_practice.sql @@ -0,0 +1,217 @@ +-- Задание 1 +EXPLAIN ANALYZE +SELECT country, count(*) AS users_count +FROM users +WHERE is_active = TRUE +GROUP BY country +HAVING count(*) > 1 +ORDER BY users_count DESC; +-- WHERE применится до GROUP BY +-- HAVING применится после GROUP BY +-- Использовать users_count в HAVING нельзя, а в ORDER BY можно + + +-- Задание 2 +EXPLAIN ANALYZE +SELECT u.name, o.order_date, o.status +FROM users u +JOIN orders o ON u.id = o.user_id +WHERE u.country = 'RU' AND o.status = 'paid' +ORDER BY o.order_date DESC; +-- Порядок выполнения по QUERY PLAN: +-- 1) сначала сканирутся таблица orders и выполняется фильтрация o.status = 'paid', +-- 2) потом сканируется таблица users по условию u.id = o.user_id, и выполняется вильтрация u.country = 'RU', +-- 3) далее Nested Loop соединяет orders и users, которые уже прошли фильтры, +-- 4) теперь выводится выполняется сортирвка o.order_date DESC + +-- Порядок выполнения по логике: +-- 1) сначала сканируем таблицу users и присоединяем orders по условию u.id = o.user_id +-- 2) потом фильтруем соединенные таблицы по u.country = 'RU' и o.status = 'paid' +-- 3) в SELECT выбираем поля которые хотим вывести - u.name, o.order_date, o.status +-- 4) сортируем выбранные поля по o.order_date по убыванию + + +-- Задание 3 +-- Запрос А +SELECT country, AVG(age) AS avg_age +FROM users +GROUP BY country +HAVING country = 'Россия'; +-- Работает медленно потому having фильтрет после группировки а не до + +-- Запрос Б +SELECT country, avg(age) AS avg_age +FROM users +WHERE country = 'RU' +GROUP BY country; +-- Этот вариант быстрее, тк он фильтрует строки до группировки, те группируется меньше строк а не все + + +-- Задание 4 +-- Запрос А +EXPLAIN ANALYZE +SELECT category, AVG(price) AS avg_price +FROM products +GROUP BY category +HAVING category = 'Электроника' AND AVG(price) > 20000; + +-- Запрос Б +EXPLAIN ANALYZE +SELECT category, AVG(price) AS avg_price +FROM products +WHERE category = 'Электроника' +GROUP BY category +HAVING AVG(price) > 20000; +-- Количество обработанных строк у запроса А: 4 +-- Количество обработанных строк у запроса Б: 4 +-- PostgreSQL походу оптимизирует даже хуйню написанную в запросе А, планы запросов А и Б одинаковые - 1 в 1, хотя по сути у запроса А должно быть больше строк + + +-- Задание 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 +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; +-- Полный порядок выполнения: +-- 1) FROM/JOIN - сканируется таблица users, и к ней присоединяется таблица orders по условию u.id = o.user_id +-- 2) WHERE - из двух соединенных таблиц фильтруются и остаются только строки где u.is_active = true +-- 3) GROUP BY - происходится группировка по u.country, те все строки с одной и той же страной(записью в столбце) сжимаются в одну +-- 4) HAVING - уже по сгруппированным строкам проводится фильтрация по COUNT(o.id) > 1, остются только те страны(строки) где кол-во пользователей больше одного +-- 5) SELECT - выбираются столбцы которые будут выведены: u.country, COUNT(o.id) AS orders_count, AVG(o.id) AS avg_order_id +-- 6) ORDER BY - выбранные в SELECT строки сортируются по orders_count по убыванию +-- 7) LIMIT - из отсортированных строк остаются только первые 3 + + +-- Задание 7 +-- Запрос А +EXPLAIN ANALYZE +SELECT category, COUNT(*) +FROM products +GROUP BY category +HAVING category = 'Электроника'; + +-- Запрос Б +EXPLAIN ANALYZE +SELECT category, COUNT(*) +FROM products +WHERE category = 'Электроника' +GROUP BY category; +-- по логике запрос Б должен быть быстрее тк он сначала филтрует строки а потом группирует уже отфильтрованное, +-- НО PostgreSql может оптимизровать и запрос А, и получается что выполняются оба запроса одинаково, сначала фильтрация а потом группировка +-- В обоих запросах на шаге 'Seq Scan on products' обрабатываются: 3 строки, а на 'GroupAggregate': 1 строка +-- Execution Time одно и тоже + + +-- Задание 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; +-- WHERE применяется между FROM/JOIN и GROUP BY +-- HAVING применяется между GROUP BY и SELECT +-- фильтрация по o.status в WHERE происходит группировки строк, а фильтрация по COUNT(o.id) в HAVING происходит уже после группирациюю, по самим сгруппированным строкам + + +-- Задание 9 +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; +-- ORDER BY применяется после SELECT, перед LIMIT/OFFSET(если есть) +-- LIMIT идет после GROUP BY, тк сначала нужно отфильтровать строки чтобы взять их кусок(5 строк) +-- если LIMIT(5) выполнить до сортировки, то это будет бесполезно, возьмутся просто первые 5 строк, и уже они будут сортироваться, и по итогу выйдет хуйня + + +-- Задание 10 +EXPLAIN ANALYZE +SELECT + category, + count(id) AS count_product_in_category, + avg(price) AS avg_category_price +FROM + products +GROUP BY + category +HAVING + count(id) >= 2 OR avg(price) > 15000 +ORDER BY + avg_category_price DESC +LIMIT 3; +-- 1) FROM - сканируем poducts +-- 2) GROUP BY - группируем по category +-- 3) HAVING - фильтруем по агрегированной функции count(id) +-- 4) SELECT - делаем выборку по столбцам category, count_product_in_category, avg_category_price +-- 5) ORDER BY - сортируем по avg_category_price по убыванию +-- 6) LIMIT - выбираем первые 3 строки из отсортированных +-- Ипсользовал HAVING тк по условию все фильтрации по агрегированным функциям +-- Выполнил с EXPLAIN ANALYZE, сначала читаетя porducts, потом делается фильтрации по count(id) >= 2 и avg(price) > 15000, далее sort, а потом Limit + + +-- Задание 11 +-- Запрос А +EXPLAIN ANALYZE +SELECT + u.name, + u.age, + (SELECT COUNT(*) + FROM orders o + WHERE o.user_id = u.id) AS orders_count +FROM users u +WHERE u.country = 'RU' +ORDER BY orders_count DESC; +-- Подзапрос в SELECT выполняется самым первым, до начала остального запроса, и называется в планировщие - SubPlan 1 +-- orders_count нельзя использовать в WHERE +-- orders_count можно использовать в ORDER BY + +-- Запрос Б(исправленный) +EXPLAIN ANALYZE +SELECT + u.name, + u.age, + count(o.id) AS orders_count +FROM users u +LEFT JOIN orders o ON u.id = o.user_id +WHERE u.country = 'RU' +GROUP BY u.name, u.age +ORDER BY orders_count DESC; +-- Запрос Б(исправленный) - быстрее, тк подзапрос в SELECT делается для каждого пользователя заново, а JOIN всего один раз \ No newline at end of file