From 8a0f9cfb5f111eadf75608a2fa1d878da5b1dab8 Mon Sep 17 00:00:00 2001 From: litoq Date: Mon, 26 Oct 2026 22:15:56 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20README.md=20=D1=81=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=D0=BC=20=D1=83=D1=87=D0=B5=D0=B1=D0=BD=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D0=BA=D0=B5=D0=B9=D1=81=D0=B0=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D1=83=D1=87=D1=91=D1=82=D1=83=20=D0=B3=D0=B0=D0=B7=D0=BE=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=BA=D0=B0=D1=87=D0=B8=D0=B2=D0=B0=D1=8E?= =?UTF-8?q?=D1=89=D0=B8=D1=85=20=D0=B0=D0=B3=D1=80=D0=B5=D0=B3=D0=B0=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B8=20=D1=80=D0=B5=D0=B7=D1=83=D0=BB=D1=8C?= =?UTF-8?q?=D1=82=D0=B0=D1=82=D0=B0=D0=BC=20=D0=BE=D1=81=D0=BC=D0=BE=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 429 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..2fc64e0 --- /dev/null +++ b/README.md @@ -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/).