diff --git a/lab_10.sql b/lab_10.sql new file mode 100644 index 0000000..3644e27 --- /dev/null +++ b/lab_10.sql @@ -0,0 +1,844 @@ +-- Лабораторная 10. +-- Задание 1. + +-- 1. В схеме schema2 создайте таблицу на основе следующего запроса: +create schema schema2; +create table schema2.test as +select + 1000000.0 * random() as random, + a::float8 as sequential, + 1.0 as value +from generate_series(1, 1000000) as a; + +select * from schema2.test; + +-- 2. Запустите сбор статистики для созданной таблицы. +analyze schema2.test; +-- Какой оператор вы использовали? +-- для сбора статистики используется оператор analyze + +-- 3. Напишите 2 запроса для получения суммы по столбцу value для поднаборов записей, +-- отфильтрованных на основе столбцов: +-- a) random (0,100) +select sum(value) +from schema2.test +where random between 0 and 100; +-- b) seq (0,100) +select sum(value) +from schema2.test +where sequential between 0 and 100; + +-- 4. Заполните таблицу базовыми значениями времени выполнения запросов +-- при разных значениях верхней границы диапазона + +CREATE TABLE schema2.table4 ( + upper_bound integer, + time_random numeric(10,3), + rows_random integer, + time_sequential numeric(10,3), + rows_sequential integer +); + + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 100; + +select count(*) +from schema2.test +where random between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100; + +select count(*) +from schema2.test +where sequential between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 1000; + +select count(*) +from schema2.test +where random between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 1000; + +select count(*) +from schema2.test +where sequential between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 10000; + +select count(*) +from schema2.test +where random between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 10000; + +select count(*) +from schema2.test +where sequential between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 100000; + +select count(*) +from schema2.test +where random between 0 and 100000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100000; + +select count(*) +from schema2.test +where sequential between 0 and 100000; + +insert into schema2.table4 ( + upper_bound, + time_random, + rows_random, + time_sequential, + rows_sequential +) values + (100,50.254,107,42.990,100), + (1000,35.000,1012,30.166,1000), + (10000,32.639,9941,32.212,10000), + (100000,33.683,99543,33.037,100000); + +select * from schema2.table4; + +-- 5. Создайте следующие индексы +create index btree_random_x + on schema2.test (random); + +create index btree_sequential_x + on schema2.test (sequential); + +create index brin_random_x + on schema2.test + using brin (random); + +create index brin_sequential_x + on schema2.test + using brin (sequential); + + +-- 6. Оцените размер табличных данных и размеры созданных индексов, +-- используя функцию pg_relation_size. Сделайте выводы +select + pg_relation_size('schema2.test') as bytes, + pg_size_pretty(pg_relation_size('schema2.test')) as size_pretty; +select + pg_relation_size('schema2.btree_random_x') as bytes, + pg_size_pretty(pg_relation_size('schema2.btree_random_x')) as size_pretty; +select + pg_relation_size('schema2.brin_random_x') as bytes, + pg_size_pretty(pg_relation_size('schema2.brin_random_x')) as size_pretty; +select + pg_relation_size('schema2.btree_sequential_x') as bytes, + pg_size_pretty(pg_relation_size('schema2.btree_sequential_x')) as size_pretty; +select + pg_relation_size('schema2.brin_sequential_x') as bytes, + pg_size_pretty(pg_relation_size('schema2.brin_sequential_x')) as size_pretty; + +create table schema2.table6 ( + obj text, + size text +); + +insert into schema2.table6 ( + obj, + size) +values + ('Таблица','50 MB'), + ('Индекс btree_random_x','21 MB'), + ('Индекс brin_random_x','24 kB'), + ('Индекс btree_sequential_x','21 MB'), + ('Индекс brin_sequential_x','24 kB'); + +select * from schema2.table6; + +-- 7. Проверьте, как изменится размер BRIN-индекса при уменьшении значения +-- pages_per_range +drop index schema2.brin_sequential_x; + +create index brin_sequential_x + on schema2.test + using brin (sequential) + with (pages_per_range = 64); + +select + pg_relation_size('schema2.brin_sequential_x') as bytes, + pg_size_pretty(pg_relation_size('schema2.brin_sequential_x')) as size_pretty; + +-- остался также 24 kB + +drop index schema2.brin_sequential_x; + +create index brin_sequential_x + on schema2.test + using brin (sequential) + with (pages_per_range = 16); + +select + pg_relation_size('schema2.brin_sequential_x') as bytes, + pg_size_pretty(pg_relation_size('schema2.brin_sequential_x')) as size_pretty; + +-- стал 32 kB + +drop index schema2.brin_sequential_x; + +create index brin_sequential_x + on schema2.test + using brin (sequential) + with (pages_per_range = 4); + +select + pg_relation_size('schema2.brin_sequential_x') as bytes, + pg_size_pretty(pg_relation_size('schema2.brin_sequential_x')) as size_pretty; + +-- стал 72 kB +-- вывод: при уменьшении pages_per_range размер увеличивается, +-- так как возрастает количество диапазонов, описываемых индексом. + +-- 8. Удалите все индексы +drop index schema2.btree_random_x; +drop index schema2.btree_sequential_x; +drop index schema2.brin_random_x; +drop index schema2.brin_sequential_x; + +-- 9. Создавая каждый индекс в отдельности, проанализируйте планы +-- выполнения и зафиксируйте время выполнения запросов (п.3) + +CREATE TABLE schema2.table9 ( + upper_bound integer, + btree_rand numeric(10,3), + btree_seq numeric(10,3), + brin_rand numeric(10,3), + brin_seq numeric(10,3) +); + +create index btree_random_x + on schema2.test (random); + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 100000; + +drop index schema2.btree_random_x; + +create index btree_sequential_x + on schema2.test (sequential); + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100000; + +drop index schema2.btree_sequential_x; + +create index brin_random_x + on schema2.test + using brin (random); + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where random between 0 and 100000; + +drop index schema2.brin_random_x; + +create index brin_sequential_x + on schema2.test + using brin (sequential); + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100000; + +insert into schema2.table9 ( + upper_bound, + btree_rand, + btree_seq, + brin_rand, + brin_seq +) values + (100,0.36,0.06,0.19,2.2), + (1000,2.5,0.65,1.2,2.6), + (10000,23,3.7,203,3.7), + (100000,99,31.7,168,33); + +select * from schema2.table9; + +-- 10. Сервер не использовал BRIN-индекс при фильтрации по столбцу random, +-- поскольку значения распределены равномерно и не коррелируют с физическим порядком +-- строк, из-за чего индекс не позволяет эффективно исключить страницы таблицы. + +-- 11. Измерьте производительность запроса, использующего фильтр по столбцу +-- sequential, при использовании различных значений параметра pages_per_range +-- (PPR) для BRIN-индекса + +CREATE TABLE schema2.table11 ( + upper_bound integer, + ppr128 numeric(10,3), + ppr64 numeric(10,3), + ppr32 numeric(10,3), + ppr16 numeric(10,3), + ppr8 numeric(10,3), + ppr4 numeric(10,3) +); + +drop index schema2.brin_sequential_x; + +create index brin_sequential_x + on schema2.test + using brin (sequential) + with (pages_per_range = 128); + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100000; + +-- 64 +drop index schema2.brin_sequential_x; + +create index brin_sequential_x + on schema2.test + using brin (sequential) + with (pages_per_range = 64); + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100000; + +-- 32 +drop index schema2.brin_sequential_x; + +create index brin_sequential_x + on schema2.test + using brin (sequential) + with (pages_per_range = 32); + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100000; + +-- 16 +drop index schema2.brin_sequential_x; + +create index brin_sequential_x + on schema2.test + using brin (sequential) + with (pages_per_range = 16); + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100000; + +-- 8 +drop index schema2.brin_sequential_x; + +create index brin_sequential_x + on schema2.test + using brin (sequential) + with (pages_per_range = 8); + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100000; + +-- 4 +drop index schema2.brin_sequential_x; + +create index brin_sequential_x + on schema2.test + using brin (sequential) + with (pages_per_range = 4); + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 1000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 10000; + +explain analyze +select sum(value) +from schema2.test +where sequential between 0 and 100000; + +insert into schema2.table11 ( + upper_bound, + ppr128, + ppr64, + ppr32, + ppr16, + ppr8, + ppr4 +) values + (100,3.9,1.17,0.6,0.7,0.3,0.4), + (1000,3.4,1.3,0.7,0.6,0.9,0.67), + (10000,3.9,3,2.7,2.8,2.7,3), + (100000,25,24,43,28,25,25); + +select * from schema2.table11; + +-- Задание 2. Методы доступа к данным + +-- 12. Создайте тестовую таблицу schema2.OrderDetails на основе следующего запроса: +create table schema2.orderdetails as +select + round(random() * 100) as order_id, + 'product ' || round(random() * 100)::char(3) as product_name, + (random() * 100)::numeric::money as price, + order_date +from generate_series( + '2022-01-01'::date, + '2023-12-31'::date, + interval '1 day' + ) as order_date, generate_series(1, 5) as id; + +-- 13. Обновите содержимое столбца order_date: +update schema2.orderdetails od +set order_date = t.min_date +from ( + select order_id, min(order_date) as min_date + from schema2.orderdetails + group by order_id +) as t +where od.order_id = t.order_id; + +-- 14. Напишите запрос, возвращающий количество записей и количество +-- уникальных товаров в каждом заказе. +select + order_id, + count(*) as rows_count, + count(distinct product_name) as unique_products +from schema2.orderdetails +group by order_id +order by order_id; + +-- a. Какой план использовался для выполнения запроса? +-- используется последовательное сканирование таблицы (seq scan) и группировка строк по order_id + +-- 15. Напишите запрос, извлекающий все записи из всех столбцов таблицы. Какой метод доступа использовался? +select * +from schema2.orderdetails; +-- последовательное сканирование + +-- 16. Напишите запрос, извлекающий заказы за 22 января 2022 года. +-- Какой метод доступа использовался? +select * +from schema2.orderdetails +where order_date = date '2022-01-22'; +-- использовалось последовательное сканирование таблицы, +-- так как по столбцу order_date отсутствует индекс, +-- и серверу необходимо просмотреть все строки, +-- чтобы выбрать записи с датой '2022-01-22' + +-- 17. Создайте B-Tree индекс на столбец order_date. +create index orderdetails_order_date_btree_idx + on schema2.orderdetails (order_date); + +-- 18. Повторите выполнение запроса из п. 5. +explain analyze +select * +from schema2.orderdetails +where order_date = date '2022-01-22'; +-- после создания btree индекса на столбец order_date сервер использовал метод доступа index scan, +-- postgres обращается к индексу orderdetails_order_date_btree_idx, чтоб быстро определить, +-- есть ли строки с указанной датой + +-- 19. Используя параметры enable_seqscan, enable_indexscan, enable_indexonlyscan, +-- enable_bitmapscan +-- a. Оцените затраты при выполнении данного запроса с использованием различных методов доступа +-- b. Cделайте выводы + +set enable_seqscan = on; +set enable_indexscan = off; +set enable_bitmapscan = off; +set enable_indexonlyscan = off; + +explain analyze +select * +from schema2.orderdetails +where order_date = date '2022-01-22'; +-- Использовано последовательное сканирование, +-- так как индексные методы доступа отключены, +-- сервер просматривает всю таблицу и отбирает подходящие строки. + +set enable_seqscan = off; +set enable_indexscan = on; +set enable_bitmapscan = off; +set enable_indexonlyscan = off; + +explain analyze +select * +from schema2.orderdetails +where order_date = date '2022-01-22'; +-- Использован index scan, +-- сервер обращается к индексу по order_date +-- и читает только строки, удовлетворяющие условию, +-- что снижает объём обрабатываемых данных по сравнению с seq scan + +set enable_seqscan = off; +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_indexonlyscan = off; + +explain analyze +select * +from schema2.orderdetails +where order_date = date '2022-01-22'; +-- Использован bitmap scan +-- сначала формируется битовая карта подходящих строк, +-- затем данные считываются из таблицы блоками. +-- Такой метод эффективен при большом числе совпадений. + +set enable_seqscan = off; +set enable_indexscan = on; +set enable_bitmapscan = on; +set enable_indexonlyscan = on; + +explain analyze +select * +from schema2.orderdetails +-- Index-only scan не применяется, +-- поскольку индекс по order_date не содержит всех столбцов, +-- используемых в SELECT *, +-- и серверу необходимо обращаться к таблице за недостающими данными. +-- В результате используется обычный index scan. + +set enable_seqscan = on; +set enable_indexscan = on; +set enable_bitmapscan = on; +set enable_indexonlyscan = on; + +-- 20. Создайте hash-индекс на столбец order_date. +create index orderdetails_order_date_hash_idx + on schema2.orderdetails + using hash (order_date); +-- 21. Повторите выполнение запроса из п.5. +explain analyze +select * +from schema2.orderdetails +where order_date = date '2022-01-22'; +-- планировщик выбрал btree индекс, так как его стоимость оказалась ниже + +-- 22. Измените условие фильтрации для извлечения всех заказов за даты, +-- предшествующие 22 января 2022 года. +explain analyze +select * +from schema2.orderdetails +where order_date < date '2022-01-22'; +-- планировщик использовал последовательное сканирование, +-- поскольку условие с диапазоном возвращает большое количество строк, +-- и применение B-tree индекса было бы менее эффективно +-- из-за частых обращений от индекса к таблице. +-- Hash-индекс не может быть задействован, +-- так как он не поддерживает операции сравнения (<, >). + +-- 23. Какой тип индекса может быть полезен для запроса, извлекающего данные ТОЛЬКО: +-- a. по заказу 34? +-- btree индекс на order_id +-- b. По заказам за январь 2022 года? +-- btree индекс на order_date +-- c. По товару +-- btree индекс на product_name + + +-- Задание 3. Способы соединения + +-- 1. Создайте новую таблицу schema2.products на основе выборки уникальных продуктов +-- из таблицы orders +create table schema2.products as +select distinct product_name +from schema2.orderdetails; + +-- 2. Добавьте в таблицу products столбец id с автоматически генерируемыми значениями (IDENTITY) +alter table schema2.products +add column id int generated always as identity; + +-- 3. Модифицируйте структуру таблицы OrderDetails: +-- a. Добавьте столбец id_product. +-- Тип данных должен соответствовать типу данных столбца id таблицы products +alter table schema2.orderdetails +add column id_product int; + +-- b. Значения в столбце id_product должны содержать код соответствующего продукта из таблицы products +update schema2.orderdetails od +set id_product = p.id +from schema2.products p +where od.product_name = p.product_name; + +-- c. Удалите столбец product_name +alter table schema2.orderdetails +drop column product_name; + +-- 4. Напишите запрос, который извлекает номер заказа, наименование продукта, цену продукта в заказе, дату заказа. +explain analyze +select + od.order_id, + p.product_name, + od.price, + od.order_date +from schema2.orderdetails od +join schema2.products p + on od.id_product = p.id; +-- a. Какой план использовался для выполнения запроса? +-- используется план с hash join между таблицами orderdetails и products, +-- b. Какой способ соединения используется в плане? +-- обе таблицы читаются последовательным сканированием, соединение выполняется по полю id_product = id + +-- 5. Создайте индексы на столбцах id_product в таблице OrderDetails и id таблицы products +create index orderdetails_id_product_idx + on schema2.orderdetails (id_product); +create index products_id_idx + on schema2.products (id); + +-- 6. Повторно выполните запрос +explain analyze +select + od.order_id, + p.product_name, + od.price, + od.order_date +from schema2.orderdetails od +join schema2.products p + on od.id_product = p.id; +-- a. Какой план использовался для выполнения запроса? +-- после создания индексов на столбцах id_product и id сервер использует план с hash join +-- таблицы считываются seq scan +-- b. Есть ли отличия от ранее использованного плана? +-- нет + +-- 7. Напишите запрос, который извлекает продукты, которых нет в 1 заказе. +-- Запрос должен возвращать номер заказа и название продукта +explain analyze +select distinct + od.order_id, + p.product_name +from schema2.orderdetails od +join schema2.products p + on od.id_product = p.id +where od.id_product not in ( + select id_product + from schema2.orderdetails + where order_id = 1 +); +-- a. Какой план использовался для выполнения запроса? +-- seq scan с использованием hash join и hashaggregate. + +-- b. Какой способ соединения используется в плане? +-- hash join. + +-- 8. Используя параметры сервера, связанные со способами объединения +-- поэкспериментируйте с выполнением запросов и обратите внимание на характеристики планов выполнения + +-- Включён только merge join +SET enable_mergejoin = on; +SET enable_hashjoin = off; +SET enable_nestloop = off; + +EXPLAIN ANALYZE +SELECT + od.order_id, + p.product_name, + od.price, + od.order_date +FROM schema2.orderdetails od +JOIN schema2.products p + ON od.id_product = p.id; +-- План использует Merge Join. +-- Для его выполнения входные данные предварительно сортируются +-- (или используются уже упорядоченные данные), +-- что при данной структуре таблиц даёт наименьшие затраты. + +-- Включён только hash join +SET enable_mergejoin = off; +SET enable_hashjoin = on; +SET enable_nestloop = off; + +EXPLAIN ANALYZE +SELECT + od.order_id, + p.product_name, + od.price, + od.order_date +FROM schema2.orderdetails od +JOIN schema2.products p + ON od.id_product = p.id; +-- План использует Hash Join. +-- PostgreSQL строит хеш-таблицу по одной из таблиц +-- и затем выполняет соединение, +-- что приводит к большим затратам по памяти и времени +-- по сравнению с merge join в данном случае.