# Домашнее задание к занятию "6.2. SQL" ## Задача 1 Используя docker поднимите инстанс PostgreSQL (версию 12) c 2 volume, в который будут складываться данные БД и бэкапы. Приведите получившуюся команду или docker-compose манифест. ### Команда получилась такой. `docker run --name postgre1 -d -e POSTGRES_HOST_AUTH_METHOD=trust -v 'C:\Users\vah\docker\07-sql:/var/lib/postgresql/data' -v 'C:\Users\vah\docker\07-sql-backup:/tmp/backup' postgres:12` подключаюсь в ту же систему так: `docker exec -it postgre1 psql -U postgres -d test_db` ## Задача 2 В БД из задачи 1: - создайте пользователя test-admin-user и БД test_db - в БД test_db создайте таблицу orders и clients (спeцификация таблиц ниже) - предоставьте привилегии на все операции пользователю test-admin-user на таблицы БД test_db - создайте пользователя test-simple-user - предоставьте пользователю test-simple-user права на SELECT/INSERT/UPDATE/DELETE данных таблиц БД test_db Таблица orders: - id (serial primary key) - наименование (string) - цена (integer) Таблица clients: - id (serial primary key) - фамилия (string) - страна проживания (string, index) - заказ (foreign key orders) Приведите: - итоговый список БД после выполнения пунктов выше, - описание таблиц (describe) - SQL-запрос для выдачи списка пользователей с правами над таблицами test_db - список пользователей с правами над таблицами test_db ### Ответ: Вот как я это делал: ``` CREATE DATABASE test_db; \c test_db CREATE USER "test-admin-user"; CREATE TABLE orders (id SERIAL PRIMARY KEY, name VARCHAR, price INT); CREATE TABLE clients (id SERIAL PRIMARY KEY, fullname VARCHAR, country VARCHAR, zakaz_id SERIAL DROP NOT NULL, FOREIGN KEY (zakaz_id) REFERENCES orders (id)); CREATE INDEX country_idx ON clients (country); GRANT ALL ON orders, clients TO "test-admin-user"; CREATE USER "test-simple-user"; GRANT SELECT,INSERT,UPDATE,DELETE ON orders, clients to "test-simple-user"; ``` Вот список таблиц: ``` test_db=# SELECT table_name FROM information_schema.tables WHERE table_schema NOT IN ('information_schema','pg_catalog'); table_name ------------ orders clients (2 rows) ``` Вот список БД с описанием: ``` test_db=# \l+ List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges | Size | Tablespace | Description -----------+----------+----------+------------+------------+-----------------------+---------+------------+-------------------------------------------- postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | 8113 kB | pg_default | default administrative connection database template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +| 7801 kB | pg_default | unmodifiable empty database | | | | | postgres=CTc/postgres | | | template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +| 7801 kB | pg_default | default template for new databases | | | | | postgres=CTc/postgres | | | test_db | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | 7801 kB | pg_default | (4 rows) ``` Список таблиц и полей: ``` test_db=# \d+ ``` Детальная информация о таблице clients ``` test_db=# \d+ clients ``` Детальная информация о таблице orders ``` test_db=# \d+ orders List of relations Schema | Name | Type | Owner | Size | Description --------+----------------------+----------+----------+------------+------------- public | clients | table | postgres | 8192 bytes | public | clients_id_seq | sequence | postgres | 8192 bytes | public | clients_zakaz_id_seq | sequence | postgres | 8192 bytes | public | orders | table | postgres | 8192 bytes | public | orders_id_seq | sequence | postgres | 8192 bytes | (5 rows) ``` Запрос и список пользователей с правами над таблицами test_db ``` SELECT table_name,grantee,privilege_type FROM information_schema.table_privileges WHERE table_schema NOT IN ('information_schema','pg_catalog'); table_name | grantee | privilege_type ------------+------------------+---------------- orders | postgres | INSERT orders | postgres | SELECT orders | postgres | UPDATE orders | postgres | DELETE orders | postgres | TRUNCATE orders | postgres | REFERENCES orders | postgres | TRIGGER clients | postgres | INSERT clients | postgres | SELECT clients | postgres | UPDATE clients | postgres | DELETE clients | postgres | TRUNCATE clients | postgres | REFERENCES clients | postgres | TRIGGER orders | test-admin-user | INSERT orders | test-admin-user | SELECT orders | test-admin-user | UPDATE orders | test-admin-user | DELETE orders | test-admin-user | TRUNCATE orders | test-admin-user | REFERENCES orders | test-admin-user | TRIGGER clients | test-admin-user | INSERT clients | test-admin-user | SELECT clients | test-admin-user | UPDATE clients | test-admin-user | DELETE clients | test-admin-user | TRUNCATE clients | test-admin-user | REFERENCES clients | test-admin-user | TRIGGER orders | test-simple-user | INSERT orders | test-simple-user | SELECT orders | test-simple-user | UPDATE orders | test-simple-user | DELETE clients | test-simple-user | INSERT clients | test-simple-user | SELECT clients | test-simple-user | UPDATE clients | test-simple-user | DELETE (36 rows) ``` Аналогичный запрос (видимо, в этих таблицах идентичные данные?) ``` test_db=# SELECT table_name,grantee,privilege_type FROM information_schema.role_table_grants WHERE table_schema NOT IN ('information_schema','pg_catalog'); ``` ## Задача 3 Используя SQL синтаксис - наполните таблицы следующими тестовыми данными: Таблица orders |Наименование|цена| |------------|----| |Шоколад| 10 | |Принтер| 3000 | |Книга| 500 | |Монитор| 7000| |Гитара| 4000| Таблица clients |ФИО|Страна проживания| |------------|----| |Иванов Иван Иванович| USA | |Петров Петр Петрович| Canada | |Иоганн Себастьян Бах| Japan | |Ронни Джеймс Дио| Russia| |Ritchie Blackmore| Russia| Используя SQL синтаксис - вычислите количество записей в каждой таблице и приведите в ответе запрос и получившийся результат. ### Ответ: ``` INSERT INTO orders (name,price) VALUES ('Шоколад',10), ('Принтер',3000), ('Книга',500), ('Монитор',7000), ('Гитара',4000); INSERT 0 5 ``` Наполнил, сначала пришлось сделать одну колонку NOT NULL `ALTER TABLE clients ALTER COLUMN zakaz_id DROP NOT NULL;` ``` INSERT INTO clients (fullname,country,zakaz_id) VALUES ('Иванов Иван Иванович','USA',NULL), ('Петров Петр Петрович','Canada',NULL), ('Иоганн Себастьян Бах','Japan',NULL), ('Ронни Джеймс Дио','Russia',NULL), ('Ritchie Blackmore','Russia',NULL); INSERT 0 5 ``` Посчитал: ``` test_db=# select count(*) from clients; count ------- 5 ``` ## Задача 4 Часть пользователей из таблицы clients решили оформить заказы из таблицы orders. Используя foreign keys свяжите записи из таблиц, согласно таблице: |ФИО|Заказ| |------------|----| |Иванов Иван Иванович| Книга | |Петров Петр Петрович| Монитор | |Иоганн Себастьян Бах| Гитара | Приведите SQL-запросы для выполнения данных операций. Приведите SQL-запрос для выдачи всех пользователей, которые совершили заказ, а также вывод данного запроса. ### Ответ: Если я правильно понял, то вот запросы на изменение. ``` UPDATE clients SET zakaz_id = (SELECT id FROM orders WHERE name = 'Книга') WHERE fullname = 'Иванов Иван Иванович'; UPDATE clients SET zakaz_id = (SELECT id FROM orders WHERE name = 'Монитор') WHERE fullname = 'Петров Петр Петрович'; UPDATE clients SET zakaz_id = (SELECT id FROM orders WHERE name = 'Гитара') WHERE fullname = 'Иоганн Себастьян Бах'; ``` ``` select * from clients; id | fullname | country | zakaz_id ----+----------------------+---------+---------- 9 | Ронни Джеймс Дио | Russia | 10 | Ritchie Blackmore | Russia | 6 | Иванов Иван Иванович | USA | 3 7 | Петров Петр Петрович | Canada | 4 8 | Иоганн Себастьян Бах | Japan | 5 select * from orders; id | name | price ----+---------+------- 1 | Шоколад | 10 2 | Принтер | 3000 3 | Книга | 500 4 | Монитор | 7000 5 | Гитара | 4000 ``` ## Задача 5 Получите полную информацию по выполнению запроса выдачи всех пользователей из задачи 4 (используя директиву EXPLAIN). Приведите получившийся результат и объясните что значат полученные значения. ### Ответ: ``` EXPLAIN select * from clients; QUERY PLAN ----------------------------------------------------------- Seq Scan on clients (cost=0.00..18.10 rows=810 width=72) (1 row) ``` **Seq Scan** — означает, что используется последовательное, блок за блоком, чтение данных таблицы `clients` **Cost** - некая виртуальная величина призванная оценить затратность операции. Первое значение 0.00 — затраты на получение первой строки. Второе — 18.10 — затраты на получение всех строк. Единица измерения cost – «извлечение одной страницы в последовательном (sequential) порядке». То есть оценивается и время, и использование ресурсов. **rows** — приблизительное количество возвращаемых строк при выполнении операции Seq Scan. Это значение возвращает планировщик. **width** - это оценка PostgreSQL того, сколько, в среднем, байт содержится в одной строке, возвращенной в рамках данной операции Как я понял, вывод этой информации - ожидания планировщика. А если дать команду `analyze` и повторить запрос, то количество строк будет более реалистичным, и cost поменяется - потому что БД проведёт анализ. ``` analyze clients; EXPLAIN select * from clients; QUERY PLAN -------------------------------------------------------- Seq Scan on clients (cost=0.00..1.05 rows=5 width=47) (1 row) ``` ## Задача 6 Создайте бэкап БД test_db и поместите его в volume, предназначенный для бэкапов (см. Задачу 1). Остановите контейнер с PostgreSQL (но не удаляйте volumes). Поднимите новый пустой контейнер с PostgreSQL. Восстановите БД test_db в новом контейнере. Приведите список операций, который вы применяли для бэкапа данных и восстановления. Ответ: Бэкап Роли `docker exec -it postgre1 pg_dumpall -U postgres --roles-only -f /tmp/backup/roles.sql` База `docker exec -it postgre1 pg_dump -h localhost -U postgres -F t -f /tmp/backup/backup_1.tar test_db` Восстановление Запускаем новый контейнер `docker run --name postgre2 -d -e POSTGRES_HOST_AUTH_METHOD=trust -v 'C:\Users\vah\docker\07-sql-2:/var/lib/postgresql/data' -v 'C:\Users\vah\docker\07-sql-backup:/tmp/backup' postgres:12` Создаём базу `docker exec -it postgre2 psql -U postgres -c "CREATE DATABASE test_db WITH ENCODING='UTF-8';"` Восстанавливаем роли из бэкапа (если нужно) `docker exec -it postgre2 psql -U postgres -f /tmp/backup/roles.sql` Восстанавливаем базу из бэкапа `docker exec -it postgre2 pg_restore -U postgres -Ft -v -d test_db /tmp/backup/backup_1.tar`