сделал DDL для пет проекта
This commit is contained in:
+260
@@ -0,0 +1,260 @@
|
||||
-- Задание 1
|
||||
-- Ситуация -- Какой JOIN выберет база? -- Почему?
|
||||
--------------------------------------------------------------------------------------
|
||||
-- 10 пользователей, 1 млн заказов,| Nested | Маленькая таблица и большая с индексом. |
|
||||
-- есть индекс на orders.user_id | Loop | База будет искать совпадения из |
|
||||
-- | | маленькой по индексу из большой. |
|
||||
--------------------------------------------------------------------------------------
|
||||
-- 100 000 пользователей, | Hash | Таблицы среднего размера, нет индексов. |
|
||||
-- 100 000 заказов, нет индексов | Join | База построит хэш-таблицу одной из них, |
|
||||
-- | | и будет искать совпадения в другой. |
|
||||
--------------------------------------------------------------------------------------
|
||||
-- Обе таблицы отсортированы по | Merge | Потому что таблицы отсортированы по |
|
||||
-- ключу, есть индексы | Join | ключу. Будет происходить соединение - |
|
||||
-- | | 'Одновременное&последовательное'. |
|
||||
--------------------------------------------------------------------------------------
|
||||
-- Маленькая таблица (5 строк) | Nested | База будет искать совпадения из |
|
||||
-- соединяется с большой (1 млн), | Loop | маленькой в большой. Но тк нет индексов |
|
||||
-- нет индекса | | - это будет медленно. |
|
||||
-- |--------| Я засомневался и разобрал с ГПТ: |
|
||||
-- | Hash | Nested Loop будет дорого(без индексов - |
|
||||
-- | Join | 5.000.000 проврок), поэтому скорее всего|
|
||||
-- | | Hash Join, просто построится хэш-таблица|
|
||||
-- | | по маленькой и 5 раз отсканирует большую|
|
||||
--------------------------------------------------------------------------------------
|
||||
-- Я заебался строить эту таблицу из пробелов. Хуй когда решусь на это снова. |
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
-- Задание 2
|
||||
-- Маленькая таблица (10 строк)
|
||||
CREATE TABLE mnoskov.small_users AS
|
||||
SELECT generate_series(1, 10) AS id, 'User ' || generate_series(1, 10) AS name;
|
||||
|
||||
-- Большая таблица (100 000 строк)
|
||||
CREATE TABLE mnoskov.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);
|
||||
--DROP INDEX idx_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: Merge Join - Потому что обе таблицы отсортированы с самого создания
|
||||
-- Варинат 2: Тоже Merge Join
|
||||
-- База могла бы выбрать Nested Loop, потому что у нас одна таблица маленькая а другая большая и с индексом, но выбрала Merge Join, потому что таблицы отсортированы
|
||||
-- С индексом Execution Time: 31.579 ms, а без примерно 80.000 ms
|
||||
|
||||
|
||||
-- Задание 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;
|
||||
-- Во втором варианте также как и в первом - Nested Loop
|
||||
-- Merge Join не появился
|
||||
-- ORDER BY может повлиять на выбор JOIN, тк если есть стортировка, то база может выбрать Merge Join
|
||||
|
||||
|
||||
-- Задание 4
|
||||
EXPLAIN ANALYZE
|
||||
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 = 'RU';
|
||||
-- (u.id = o.user_id) - Nested Loop; (o.id = oi.order_id) - Nested Loop; (oi.product_id = p.id) - Hash Join
|
||||
-- Seq Scan на этапах сканирования orders и products
|
||||
-- Думаю стоит создать индекс на orders.user_id; и надо было бы на users.countyr, но я создавал уже в предидущих укроках; подумал, что надо ещё products.id, но там id и так первичный ключ, значит и индекс есть
|
||||
CREATE INDEX idx_orders_user_id ON orders(user_id); -- DROP INDEX idx_orders_user_id;
|
||||
-- После создания индекса типы JOIN не поменялись, план остался тем же, скорость выполнения запроса та же
|
||||
|
||||
|
||||
-- Задание 5
|
||||
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 = 'RU'
|
||||
GROUP BY u.name;
|
||||
-- на этапе LEFT JOIN order_items oi ON o.id = oi.order_id - оптимизатор выполнил Hash Right Join по условию oi.order_id = o.id, то есть Hash Join
|
||||
-- на этапе LEFT JOIN orders o ON u.id = o.user_id - оптимизатор выполнил Hash Left Join по условию u.id = o.user_id, то есть тоже Hash Join
|
||||
-- Метод по сути один и тот же - Hash Join, но отличаются Right и Left - я думаю это выбрано для лучшей оптимизации
|
||||
-- Через Hash Right Join проходит - 12 строк
|
||||
-- Через Hash Left Join проходит - 11281 строк
|
||||
|
||||
|
||||
-- Задание 6
|
||||
-- Вариант 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;
|
||||
-- Разный тип используется, в варианте А - Nested Loop, в варианте B - Hash Left Join
|
||||
-- Да, у варанта B Execution Time в очень много раз больше
|
||||
-- LEFT JOIN медленне, потому что он не использует индекс, а делает Seq Scan, те читает таблицы полностью, потому что подругому не получится выполнить LEFT JOIN
|
||||
|
||||
|
||||
-- Задание 7
|
||||
-- Вариант 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;
|
||||
-- Тип JOIN не изменился
|
||||
-- Порядок чтения таблиц не изменился
|
||||
-- Execution Time одинаковое
|
||||
-- Оптимизатор выбрал один и тот же порядок потому что по сути это два одинаковых запроса, с одной и той же логикой, но написаны чуть чуть по разному, и оптимизатор в обоих случаях выполняет так, как быстрее. Почему порядок может быть разным, я не предполагаю.
|
||||
|
||||
|
||||
-- Задание 8
|
||||
-- Запрос 1
|
||||
EXPLAIN ANALYZE
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
orders o
|
||||
JOIN
|
||||
order_items oi ON o.id = oi.order_id
|
||||
ORDER BY
|
||||
o.id, oi.order_id;
|
||||
-- Уверен такой запрос привел бы к Merge Join, если бы данных в таблицах было побольше
|
||||
|
||||
-- Запрос 2(из задания 2)
|
||||
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;
|
||||
-- Потому что обе таблицы уже отсортированы в момент создания
|
||||
|
||||
-- Задание 9
|
||||
-- Nested Loop лучше Hash Join, когда есть индексы по полям фильтрации, когда одна таблица небольшая, а у второй есть индекс. Подглядел: когда нужно найти немного совпадений.
|
||||
-- Hash Join лучше чем Nested Loop, когда нет индексов, когда таблицы среднего размера и больше, и когдау условие фильтрации '='
|
||||
|
||||
-- Оба запроса из задания 6:
|
||||
-- Вариант A: INNER JOIN
|
||||
EXPLAIN ANALYZE
|
||||
SELECT u.name, o.order_date
|
||||
FROM users u
|
||||
INNER JOIN orders o ON u.id = o.user_id;
|
||||
-- тут nested loop быстрее, тк соеденияется все по индексам
|
||||
|
||||
-- Вариант B: LEFT JOIN
|
||||
EXPLAIN ANALYZE
|
||||
SELECT u.name, o.order_date
|
||||
FROM users u
|
||||
LEFT JOIN orders o ON u.id = o.user_id;
|
||||
-- а тут быстрее merge join, тк по индексам соединить не получится, тут нужны обе полные таблицы, и создается по одной из них хэш-таблица
|
||||
|
||||
|
||||
-- Задание 10
|
||||
-- Запрос A
|
||||
EXPLAIN ANALYZE
|
||||
--CREATE MATERIALIZED VIEW mv_category_revenue AS
|
||||
SELECT
|
||||
p.category,
|
||||
sum(oi.quantity * oi.price_at_purchase) AS total_revenue
|
||||
FROM
|
||||
mnoskov.products p
|
||||
JOIN
|
||||
mnoskov.order_items oi ON p.id = oi.product_id
|
||||
JOIN
|
||||
mnoskov.orders o ON oi.order_id = o.id
|
||||
WHERE o.status = 'paid'
|
||||
GROUP BY
|
||||
p.category
|
||||
ORDER BY
|
||||
total_revenue desc;
|
||||
-- До: Execution Time: 0.309 ms
|
||||
CREATE INDEX idx_orders_status ON orders(status);
|
||||
-- После добавления индекса ничего не поменялось, план запроса остался тот же, скорее всего потому что мало данных, думаю будь их больше с индексом запрос был бы быстрее
|
||||
|
||||
-- Запрос B
|
||||
EXPLAIN ANALYZE
|
||||
--CREATE MATERIALIZED VIEW mv_user_activity AS
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.name AS user_name,
|
||||
count(*) AS orders_count
|
||||
FROM
|
||||
mnoskov.users u
|
||||
JOIN
|
||||
mnoskov.orders o ON u.id = o.user_id
|
||||
WHERE
|
||||
o.status = 'paid'
|
||||
GROUP BY
|
||||
u.id, u.name
|
||||
HAVING
|
||||
count(*) > 0
|
||||
ORDER BY
|
||||
orders_count desc;
|
||||
-- И тут тоже несмотря на то что индекс на orders статус я уже создал, таблица orders скаринруется как Seq Scan, я думаю это потому, что мало данных, будь их больше с индексом запрос был бы быстрее
|
||||
|
||||
-- Запрос C
|
||||
EXPLAIN ANALYZE
|
||||
--CREATE MATERIALIZED VIEW mv_daily_revenue AS
|
||||
SELECT
|
||||
o.order_date::date AS sales_date,
|
||||
sum(oi.quantity * oi.price_at_purchase) AS daily_revenue
|
||||
FROM
|
||||
mnoskov.orders o
|
||||
JOIN
|
||||
mnoskov.order_items oi ON o.id = oi.order_id
|
||||
WHERE
|
||||
o.status = 'paid'
|
||||
GROUP BY
|
||||
o.order_date
|
||||
ORDER BY
|
||||
sales_date;
|
||||
-- Тут ситуация аналогичная и для первый двух запросов, нужные индексы уже созданы, в связи выполнения всех предидущих занятий и тем, и единственное что я мог сделать для улучшения времени выполнения - это добавить индекс на orders(status)
|
||||
Reference in New Issue
Block a user