ДЗ
Запуск PostgreSQL в Docker. Создание таблиц..
По ходу выполнения сохраняйте команды и вывод каждого шага — это и есть содержимое `README.md`, собирать всё в конце сложнее.
-----------
### Шаг 1. Поднять контейнер PostgreSQL
Запустите PostgreSQL через Docker:
```bash
docker run --name pg-shop \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=shop \
-p 5432:5432 \
-d postgres:16
```
Убедитесь, что контейнер запустился: `docker ps`.
-----------
### Шаг 2. Подключиться к базе данных
Выберите удобный инструмент: DBeaver, pgAdmin или `psql`. Параметры подключения:
- хост: `localhost`
- порт: `5432`
- база: `shop`
- пользователь: `postgres`
- пароль: `secret`
-----------
### Шаг 3. Создать три таблицы
В файле `schema.sql` напишите и выполните DDL-скрипт:
```sql
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
CREATE TABLE products (
product_id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
price NUMERIC(10,2) NOT NULL CHECK (price >= 0)
);
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT NOT NULL REFERENCES customers(customer_id),
created_at TIMESTAMP NOT NULL DEFAULT now()
);
```
Требования к таблицам:
- у каждой таблицы — первичный ключ (`PRIMARY KEY`);
- `orders.customer_id` — внешний ключ (`FOREIGN KEY`) на `customers`;
- типы данных разумные: `NUMERIC` для цены, `TIMESTAMP` для даты.
-----------
### Шаг 4. Вставить тестовые данные
В файле `queries.sql` напишите `INSERT` для 3–5 строк в каждую таблицу. Соблюдайте порядок: сначала `customers` и `products`, потом `orders` (иначе нарушится ссылочная целостность).
Пример:
```sql
INSERT INTO customers (name, email) VALUES
('Иван Петров', 'ivan@example.com'),
('Мария Сидорова', 'maria@example.com'),
('Пётр Кузнецов', 'petr@example.com');
INSERT INTO products (title, price) VALUES
('Ноутбук', 89999.00),
('Мышь', 1499.00),
('Монитор', 24999.00);
INSERT INTO orders (customer_id) VALUES (1), (1), (2), (3);
```
-----------
### Шаг 5. Написать запросы
В том же файле `queries.sql` напишите:
1. Два-три `SELECT` с `WHERE` и `ORDER BY`, например:
- все товары дороже 2000 рублей, отсортированные по цене;
- все заказы одного клиента (`WHERE customer_id = ...`), отсортированные по дате.
2. Один `JOIN` — клиент вместе со своими заказами:
```sql
SELECT c.name, c.email, o.order_id, o.created_at
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id
ORDER BY c.name;
```
-----------
### Форма сдачи
GitHub-репозиторий с тремя файлами:
- `schema.sql` — DDL: `CREATE TABLE`;
- `queries.sql` — `INSERT` и `SELECT`;
- `README.md` — команда запуска контейнера, как подключиться, скриншот или текстовый вывод запросов.
-----------
### Куда сдать
Форма сдачи — в карточке занятия на сайте OTUS (личный кабинет).
-----------
### Куда задавать вопросы
Чат группы в VK или лично преподавателю: https://vk.com/rafraf
-----------
### Чем можно пользоваться
- Материалы лекции
- Документация PostgreSQL: https://www.postgresql.org/docs/
- Образ `postgres` на Docker Hub: https://hub.docker.com/_/postgres
- DBeaver: https://dbeaver.io/
-----------
-----------
-----------
## Бонусное ДЗ
На выбор (можно выполнить несколько):
**Вариант A — таблица `order_items` (M:N):**
Добавьте таблицу, связывающую заказы и товары:
```sql
CREATE TABLE order_items (
order_id INT NOT NULL REFERENCES orders(order_id),
product_id INT NOT NULL REFERENCES products(product_id),
qty INT NOT NULL CHECK (qty > 0),
PRIMARY KEY (order_id, product_id)
);
```
Вставьте несколько строк в `order_items` (иначе агрегат ниже вернёт пусто):
```sql
INSERT INTO order_items (order_id, product_id, qty) VALUES
(1, 1, 2), (1, 2, 1), (2, 3, 1);
```
Напишите `JOIN` через `order_items` и агрегат — сумму заказа:
```sql
SELECT o.order_id, c.name, SUM(p.price * oi.qty) AS total
FROM orders o
JOIN customers c ON c.customer_id = o.customer_id
JOIN order_items oi ON oi.order_id = o.order_id
JOIN products p ON p.product_id = oi.product_id
GROUP BY o.order_id, c.name
ORDER BY o.order_id;
```
**Вариант B — ограничения:**
Добавьте `UNIQUE` или `CHECK` к любой таблице и объясните в `README.md`, зачем это нужно.
**Вариант C — индекс и EXPLAIN:**
На таблицах из 3–5 строк работу индекса не увидеть, поэтому сгенерируйте таблицу побольше:
```sql
CREATE TABLE big_orders (
order_id SERIAL PRIMARY KEY,
customer_id INT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT now()
);
INSERT INTO big_orders (customer_id)
SELECT (random() * 10000)::int
FROM generate_series(1, 100000);
```
Посмотрите план запроса без индекса:
```sql
EXPLAIN SELECT * FROM big_orders WHERE customer_id = 42;
```
Создайте индекс и повторите тот же `EXPLAIN`:
```sql
CREATE INDEX idx_big_orders_customer_id ON big_orders (customer_id);
```
В `README.md` приложите оба плана и объясните разницу: какой способ доступа PostgreSQL выбрал до индекса и после (`Seq Scan` → `Bitmap Index Scan` / `Index Scan`) и почему.