Добавлен README.md с описанием учебного кейса по учёту газоперекачивающих агрегатов и результатам осмотров

This commit is contained in:
2026-10-26 22:15:56 +03:00
commit 8a0f9cfb5f
+429
View File
@@ -0,0 +1,429 @@
# Учебный кейс «Учёт газоперекачивающих агрегатов и результатов осмотров»
## 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/).