430 lines
22 KiB
Markdown
430 lines
22 KiB
Markdown
# Учебный кейс «Учёт газоперекачивающих агрегатов и результатов осмотров»
|
||
|
||
## 1. Описание ситуации
|
||
|
||
На двух компрессорных станциях эксплуатируются газоперекачивающие агрегаты (ГПА). Сотрудники проводят осмотры и фиксируют результат: «Без замечаний» или «Есть замечания», при необходимости добавляют комментарий.
|
||
|
||
Сведения ведутся в общей электронной таблице: названия станций и оборудования повторяются в каждой строке. Необходимо создать небольшую базу данных, чтобы исключить лишнее повторение сведений и получать ответы на вопросы руководителя.
|
||
|
||
Все названия, записи и результаты осмотров вымышлены. Кейс не предназначен для диагностики оборудования.
|
||
|
||
## 2. Что должна позволять база данных
|
||
|
||
- Хранить перечень станций и установленного оборудования.
|
||
- Регистрировать осмотры.
|
||
- Находить осмотры с замечаниями.
|
||
- Получать сводку по каждой станции.
|
||
|
||
## 3. Структура данных
|
||
|
||
В учебной модели три таблицы:
|
||
|
||
| Таблица | Что хранится | Поля |
|
||
|---|---|---|
|
||
| `stations` | Компрессорные станции | `station_id`, `name` |
|
||
| `units` | Газоперекачивающие агрегаты | `unit_id`, `station_id`, `inventory_no`, `commissioned_year` |
|
||
| `inspections` | История осмотров | `inspection_id`, `unit_id`, `inspection_date`, `result`, `comment` |
|
||
|
||
`id` — идентификатор записи. Первичный ключ однозначно определяет запись; внешний ключ связывает её с записью другой таблицы.
|
||
|
||
```text
|
||
Компрессорная станция (1) ── (N) Агрегат (1) ── (N) Осмотр
|
||
```
|
||
|
||
Одна станция может содержать несколько агрегатов; у агрегата может быть несколько осмотров. В этом кейсе агрегат закреплён за одной станцией. Историю перемещений, сотрудников, ремонты и датчики не рассматриваем.
|
||
|
||
Исходные данные: 2 станции, 4 агрегата, 6 осмотров. Данные для загрузки приведены в SQL и JSON ниже — создавать их самостоятельно не требуется.
|
||
|
||
## 4. Создание диаграммы — готовый код
|
||
|
||
Выполните этот шаг до перехода к SQL-заданиям.
|
||
|
||
1. Откройте [генератор диаграмм dbdiagram](https://dbdiagram.io/erd-generator).
|
||
2. Найдите редактор схемы. Выберите DBML, если сервис предлагает выбор формата.
|
||
3. Удалите демонстрационный пример и вставьте весь код из блока ниже. Строки с обозначением языка блока копировать не нужно: копируйте только сам код.
|
||
4. Схема должна появиться рядом с редактором. При необходимости перейдите из страницы генератора в интерактивный редактор по предложенной сервисом ссылке.
|
||
5. Найдите три таблицы и две линии связей. Сохраните скриншот диаграммы.
|
||
|
||
DBML — текстовое описание структуры. Этот код рисует диаграмму, но не создаёт таблицы в PostgreSQL. Регистрация для просмотра интерактивной ERD по описанию сервиса не требуется; дополнительные функции полного редактора могут требовать учётную запись.
|
||
|
||
```dbml
|
||
Table stations {
|
||
station_id integer [pk, note: 'Идентификатор станции']
|
||
name text [not null, note: 'Название станции']
|
||
Note: 'Компрессорные станции'
|
||
}
|
||
|
||
Table units {
|
||
unit_id integer [pk, note: 'Идентификатор агрегата']
|
||
station_id integer [not null, note: 'Станция установки']
|
||
inventory_no text [not null, unique, note: 'Инвентарный номер']
|
||
commissioned_year integer [not null, note: 'Год ввода в эксплуатацию']
|
||
Note: 'Газоперекачивающие агрегаты'
|
||
}
|
||
|
||
Table inspections {
|
||
inspection_id integer [pk, note: 'Идентификатор осмотра']
|
||
unit_id integer [not null, note: 'Осмотренный агрегат']
|
||
inspection_date date [not null, note: 'Дата осмотра']
|
||
result text [not null, note: 'Без замечаний или Есть замечания']
|
||
comment text [note: 'Комментарий']
|
||
Note: 'Журнал осмотров'
|
||
}
|
||
|
||
Ref: units.station_id > stations.station_id
|
||
Ref: inspections.unit_id > units.unit_id
|
||
```
|
||
|
||
Проверьте результат:
|
||
|
||
- В `units` поле `station_id` связано с `stations.station_id`.
|
||
- В `inspections` поле `unit_id` связано с `units.unit_id`.
|
||
- Название станции не дублируется в каждой строке осмотра.
|
||
|
||
Три уровня проектирования на этом примере:
|
||
|
||
- Концептуальный: станция, агрегат, осмотр и связи между ними.
|
||
- Логический: атрибуты, идентификаторы и структура связей.
|
||
- Физический: конкретные таблицы, типы данных и ограничения PostgreSQL в следующем разделе.
|
||
|
||
Диаграмма выше показывает уже детализированную структуру; для каждого уровня отдельную диаграмму строить не требуется.
|
||
|
||
## 5. Создание и заполнение базы PostgreSQL
|
||
|
||
1. Откройте [Aiven PostgreSQL Playground](https://aiven.io/tools/pg-playground).
|
||
2. Перейдите к SQL-редактору, удалите демонстрационный код.
|
||
3. Вставьте весь следующий SQL-скрипт и выполните его кнопкой запуска в интерфейсе.
|
||
4. Дождитесь завершения без ошибок.
|
||
5. Сохраните скрипт у себя в текстовом файле. Не обновляйте и не закрывайте вкладку во время работы.
|
||
|
||
> Внимание: первые три команды удаляют учебные таблицы и их содержимое. Скрипт нужен для первоначального создания БД и возврата к исходным данным. Не применяйте его к реальной рабочей базе.
|
||
|
||
```sql
|
||
DROP TABLE IF EXISTS inspections;
|
||
DROP TABLE IF EXISTS units;
|
||
DROP TABLE IF EXISTS stations;
|
||
|
||
CREATE TABLE stations (
|
||
station_id INTEGER PRIMARY KEY,
|
||
name TEXT NOT NULL
|
||
);
|
||
|
||
CREATE TABLE units (
|
||
unit_id INTEGER PRIMARY KEY,
|
||
station_id INTEGER NOT NULL REFERENCES stations(station_id),
|
||
inventory_no TEXT UNIQUE NOT NULL,
|
||
commissioned_year INTEGER NOT NULL
|
||
);
|
||
|
||
CREATE TABLE inspections (
|
||
inspection_id INTEGER PRIMARY KEY,
|
||
unit_id INTEGER NOT NULL REFERENCES units(unit_id),
|
||
inspection_date DATE NOT NULL,
|
||
result TEXT NOT NULL CHECK (result IN ('Без замечаний', 'Есть замечания')),
|
||
comment TEXT
|
||
);
|
||
|
||
INSERT INTO stations (station_id, name) VALUES
|
||
(1, 'КС «Северная»'),
|
||
(2, 'КС «Южная»');
|
||
|
||
INSERT INTO units (unit_id, station_id, inventory_no, commissioned_year) VALUES
|
||
(1, 1, 'ГПА-001', 2018),
|
||
(2, 1, 'ГПА-002', 2020),
|
||
(3, 2, 'ГПА-003', 2019),
|
||
(4, 2, 'ГПА-004', 2022);
|
||
|
||
INSERT INTO inspections (inspection_id, unit_id, inspection_date, result, comment) VALUES
|
||
(1, 1, '2026-10-20', 'Без замечаний', 'Видимых замечаний нет'),
|
||
(2, 2, '2026-10-20', 'Есть замечания', 'Повреждена маркировка'),
|
||
(3, 3, '2026-10-20', 'Есть замечания', 'Зафиксирован посторонний шум'),
|
||
(4, 4, '2026-10-20', 'Без замечаний', 'Видимых замечаний нет'),
|
||
(5, 2, '2026-10-21', 'Без замечаний', 'Маркировка восстановлена'),
|
||
(6, 3, '2026-10-21', 'Есть замечания', 'Требуется дополнительная проверка');
|
||
```
|
||
|
||
### Проверка загрузки
|
||
|
||
Замените текст в редакторе проверочным запросом и выполните его. Выполненный код создания не нужно повторять перед каждым запросом; при необходимости сохраните его отдельно и оставляйте в редакторе только текущий запрос.
|
||
|
||
```sql
|
||
SELECT 'stations' AS table_name, COUNT(*) AS row_count FROM stations
|
||
UNION ALL
|
||
SELECT 'units', COUNT(*) FROM units
|
||
UNION ALL
|
||
SELECT 'inspections', COUNT(*) FROM inspections;
|
||
```
|
||
|
||
Ожидается: `stations` — 2, `units` — 4, `inspections` — 6.
|
||
|
||
Если после очистки редактора сервис начинает новую сессию и таблицы недоступны, используйте в одном запуске исходный скрипт и текущий запрос; учитывайте, что это каждый раз сбрасывает внесённые изменения. Для заданий добавления и исправления записи выполняйте все связанные команды вместе после исходного скрипта. Резервная площадка — [db<>fiddle](https://dbfiddle.uk/), выберите PostgreSQL: создание и загрузку поместите в первый блок, запросы — в следующие.
|
||
|
||
## 6. Работа с реляционной базой: задания с решениями
|
||
|
||
Сначала выполните задания 1–6 на исходных данных. Затем переходите к добавлению и изменению записей. Каждый блок ниже можно скопировать в SQL-редактор.
|
||
|
||
### Задание 1. Просмотреть все агрегаты
|
||
|
||
Решение:
|
||
|
||
```sql
|
||
SELECT *
|
||
FROM units
|
||
ORDER BY unit_id;
|
||
```
|
||
|
||
Ожидаемый результат и объяснение: Появятся четыре строки: ГПА-001, ГПА-002, ГПА-003 и ГПА-004. `SELECT` означает «выбрать», `*` — все столбцы, `FROM` — из какой таблицы.
|
||
|
||
### Задание 2. Найти агрегаты, введённые после 2019 года
|
||
|
||
Решение:
|
||
|
||
```sql
|
||
SELECT inventory_no, commissioned_year
|
||
FROM units
|
||
WHERE commissioned_year > 2019
|
||
ORDER BY inventory_no;
|
||
```
|
||
|
||
Ожидаемый результат и объяснение: Две строки: ГПА-002 — 2020; ГПА-004 — 2022. `WHERE` задаёт условие. Значение 2019 не включается.
|
||
|
||
### Задание 3. Найти осмотры с замечаниями
|
||
|
||
Решение:
|
||
|
||
```sql
|
||
SELECT inspection_id, unit_id, inspection_date, comment
|
||
FROM inspections
|
||
WHERE result = 'Есть замечания'
|
||
ORDER BY inspection_id;
|
||
```
|
||
|
||
Ожидаемый результат и объяснение: Три записи: осмотры № 2, 3 и 6. Это число событий, а не число неисправных агрегатов.
|
||
|
||
### Задание 4. Показать станцию каждого агрегата
|
||
|
||
Решение:
|
||
|
||
```sql
|
||
SELECT u.inventory_no, s.name AS station
|
||
FROM units AS u
|
||
JOIN stations AS s ON s.station_id = u.station_id
|
||
ORDER BY u.inventory_no;
|
||
```
|
||
|
||
Ожидаемый результат и объяснение: ГПА-001 и ГПА-002 относятся к КС «Северная», ГПА-003 и ГПА-004 — к КС «Южная». `JOIN` соединяет таблицы, `ON` указывает связь. `u` и `s` — короткие обозначения таблиц, `AS station` — подпись столбца результата.
|
||
|
||
### Задание 5. Получить подробный список замечаний
|
||
|
||
Решение:
|
||
|
||
```sql
|
||
SELECT i.inspection_date,
|
||
s.name AS station,
|
||
u.inventory_no,
|
||
i.comment
|
||
FROM inspections AS i
|
||
JOIN units AS u ON u.unit_id = i.unit_id
|
||
JOIN stations AS s ON s.station_id = u.station_id
|
||
WHERE i.result = 'Есть замечания'
|
||
ORDER BY i.inspection_date, u.inventory_no;
|
||
```
|
||
|
||
Ожидаемый результат и объяснение: Три строки: ГПА-002 на КС «Северная» — 20.10.2026; ГПА-003 на КС «Южная» — 20.10.2026 и 21.10.2026. Название станции получено через две связи.
|
||
|
||
### Задание 6. Получить сводку по всем станциям
|
||
|
||
Решение:
|
||
|
||
```sql
|
||
SELECT s.name AS station,
|
||
COUNT(i.inspection_id) AS inspections_total,
|
||
SUM(CASE WHEN i.result = 'Есть замечания'
|
||
THEN 1 ELSE 0 END) AS inspections_with_remarks
|
||
FROM stations AS s
|
||
LEFT JOIN units AS u ON u.station_id = s.station_id
|
||
LEFT JOIN inspections AS i ON i.unit_id = u.unit_id
|
||
GROUP BY s.station_id, s.name
|
||
ORDER BY s.station_id;
|
||
```
|
||
|
||
Ожидаемый результат и объяснение: КС «Северная»: всего 3 осмотра, с замечаниями 1. КС «Южная»: всего 3 осмотра, с замечаниями 2. `GROUP BY` объединяет записи по станции. `COUNT` считает осмотры. `SUM(CASE ...)` считает только записи с замечаниями. `LEFT JOIN` позволяет сохранить в отчёте даже станцию без осмотров.
|
||
|
||
### Задание 7. Зарегистрировать новый осмотр
|
||
|
||
Добавьте осмотр ГПА-001 от 22.10.2026. В комментарии специально оставлена опечатка для следующего задания.
|
||
|
||
```sql
|
||
INSERT INTO inspections
|
||
(inspection_id, unit_id, inspection_date, result, comment)
|
||
VALUES
|
||
(7, 1, '2026-10-22', 'Без замечаний', 'Осмотр заврешён');
|
||
|
||
SELECT * FROM inspections WHERE inspection_id = 7;
|
||
```
|
||
|
||
Ожидается новая запись № 7. Общее число осмотров увеличится с 6 до 7. Этот `INSERT` выполняйте один раз: повторная вставка того же идентификатора вызовет ошибку первичного ключа.
|
||
|
||
### Задание 8. Исправить комментарий
|
||
|
||
```sql
|
||
UPDATE inspections
|
||
SET comment = 'Осмотр завершён'
|
||
WHERE inspection_id = 7;
|
||
|
||
SELECT * FROM inspections WHERE inspection_id = 7;
|
||
```
|
||
|
||
Ожидается: у записи № 7 комментарий «Осмотр завершён». `UPDATE` изменяет запись, `WHERE` ограничивает изменение конкретной записью. Не убирайте условие: иначе комментарий изменится у всех осмотров.
|
||
|
||
Повторите сводку из задания 6. После добавления осмотра у КС «Северная» будет 4 осмотра, из них 1 с замечаниями; у КС «Южная» — 3 и 2 соответственно.
|
||
|
||
## 7. Работа с документной моделью: задания с решениями
|
||
|
||
Используем тот же исходный набор из шести осмотров, до добавления записи № 7. Здесь каждый осмотр — отдельный документ. Название станции и номер агрегата записаны прямо внутри него. Это альтернативная учебная модель с намеренным повторением части сведений.
|
||
|
||
### Шаг 1. Загрузить документы
|
||
|
||
1. Откройте [Mongo Playground](https://mongoplayground.net/).
|
||
2. Выберите режим `single collection` и формат `bson`, если предлагается выбор.
|
||
3. В поле `Database` удалите пример и вставьте весь массив ниже.
|
||
4. В поле `Query` вставьте запрос из следующего шага.
|
||
5. Выполните запрос командой запуска в интерфейсе или сочетанием `Ctrl + Enter` (`Cmd + Enter` на macOS).
|
||
|
||
Поле Database задаёт исходные документы песочницы. Команда SQL или `insertMany()` здесь не нужна.
|
||
|
||
```json
|
||
[
|
||
{
|
||
"inspection_id": 1,
|
||
"date": "2026-10-20",
|
||
"station": "КС «Северная»",
|
||
"unit": "ГПА-001",
|
||
"result": "Без замечаний",
|
||
"details": {
|
||
"observation": "Видимых замечаний нет"
|
||
}
|
||
},
|
||
{
|
||
"inspection_id": 2,
|
||
"date": "2026-10-20",
|
||
"station": "КС «Северная»",
|
||
"unit": "ГПА-002",
|
||
"result": "Есть замечания",
|
||
"details": {
|
||
"observation": "Повреждена маркировка"
|
||
}
|
||
},
|
||
{
|
||
"inspection_id": 3,
|
||
"date": "2026-10-20",
|
||
"station": "КС «Южная»",
|
||
"unit": "ГПА-003",
|
||
"result": "Есть замечания",
|
||
"details": {
|
||
"observation": "Зафиксирован посторонний шум"
|
||
}
|
||
},
|
||
{
|
||
"inspection_id": 4,
|
||
"date": "2026-10-20",
|
||
"station": "КС «Южная»",
|
||
"unit": "ГПА-004",
|
||
"result": "Без замечаний",
|
||
"details": {
|
||
"observation": "Видимых замечаний нет"
|
||
}
|
||
},
|
||
{
|
||
"inspection_id": 5,
|
||
"date": "2026-10-21",
|
||
"station": "КС «Северная»",
|
||
"unit": "ГПА-002",
|
||
"result": "Без замечаний",
|
||
"details": {
|
||
"observation": "Маркировка восстановлена"
|
||
}
|
||
},
|
||
{
|
||
"inspection_id": 6,
|
||
"date": "2026-10-21",
|
||
"station": "КС «Южная»",
|
||
"unit": "ГПА-003",
|
||
"result": "Есть замечания",
|
||
"details": {
|
||
"observation": "Требуется дополнительная проверка"
|
||
}
|
||
}
|
||
]
|
||
```
|
||
|
||
### Шаг 2. Показать все осмотры
|
||
|
||
Вставьте в поле Query:
|
||
|
||
```javascript
|
||
db.collection.find({})
|
||
```
|
||
|
||
Ожидается: 6 документов. Дата в примере хранится строкой `YYYY-MM-DD`, а не специальным типом даты MongoDB.
|
||
|
||
### Шаг 3. Найти осмотры с замечаниями
|
||
|
||
```javascript
|
||
db.collection.find({
|
||
"result": "Есть замечания"
|
||
})
|
||
```
|
||
|
||
Ожидается: 3 документа — `inspection_id` 2, 3 и 6.
|
||
|
||
### Шаг 4. Найти историю выбранного агрегата
|
||
|
||
```javascript
|
||
db.collection.find({
|
||
"unit": "ГПА-003"
|
||
})
|
||
```
|
||
|
||
Ожидается: 2 документа — осмотры № 3 и 6.
|
||
|
||
### Шаг 5. Добавить поле к одному исходному документу
|
||
|
||
В поле Database найдите первый документ (`inspection_id` 1). После строки `"inspection_id": 1,` добавьте строку:
|
||
|
||
```json
|
||
"photo": "inspection_01.jpg",
|
||
```
|
||
|
||
Не удаляйте запятые между полями. Затем выполните:
|
||
|
||
```javascript
|
||
db.collection.find({
|
||
"inspection_id": 1
|
||
})
|
||
```
|
||
|
||
Ожидается: документ № 1 с новым полем `photo`. Оно содержит имя файла, не саму фотографию. Это редактирование исходного набора песочницы; изменение записи командой СУБД в реляционной части показано отдельно через `UPDATE`.
|
||
|
||
### Шаг 6. Получить сводку по замечаниям
|
||
|
||
Дополнительное задание; если времени недостаточно, его демонстрирует преподаватель.
|
||
|
||
```javascript
|
||
db.collection.aggregate([
|
||
{ "$match": { "result": "Есть замечания" } },
|
||
{ "$group": { "_id": "$station", "remarks_count": { "$sum": 1 } } },
|
||
{ "$sort": { "_id": 1 } }
|
||
])
|
||
```
|
||
|
||
Ожидается: КС «Северная» — 1; КС «Южная» — 2. `$match` отбирает документы, `$group` объединяет по станции, `$sum` считает, `$sort` упорядочивает. В этой сводке появятся только станции, у которых есть замечания; станции без замечаний она не выводит.
|
||
|
||
## 8. Ссылки на инструменты
|
||
|
||
- [dbdiagram: генератор диаграмм](https://dbdiagram.io/erd-generator) — описание сервиса подтверждает приём DBML и SQL.
|
||
- [PostgreSQL Playground](https://aiven.io/tools/pg-playground).
|
||
- [Mongo Playground](https://mongoplayground.net/).
|
||
- [Документация Mongo Playground](https://mongoplayground.net/static/docs-8.html).
|
||
- [Резервная SQL-площадка db<>fiddle](https://dbfiddle.uk/).
|