
В небольшой автомастерской каждый день куча информации: клиенты, машины, заказы, услуги, запчасти на складе, поставщики. Когда всё это в бумажных журналах или Excel-файлах, искать что-то нужное долго, а ошибки накапливаются.
Я сделал реляционную базу данных, которая собирает все эти потоки в одном месте. 11 таблиц связаны между собой, есть нормализация до 3НФ, целостность через внешние ключи и SQL-запросы под основные задачи.
Работа состоит из трёх частей: теория, проектирование и реализация в MySQL. Если написать клиентское приложение, базу можно реально использовать в работающей мастерской.
На каждую сущность отдельная таблица, чтобы не было дубликатов и было проще поддерживать. Связи между таблицами идут через внешние ключи, которые сама СУБД проверяет на целостность.
→ Прокрути дальше, чтобы пролистать схему
Три из этих таблиц служат связками. Они реализуют связь «многие ко многим»: в одном заказе много услуг и запчастей, а одна услуга или запчасть фигурирует во многих заказах.
В реляционной модели каждая запись знает о других через ключи. Простая связь, цепочка или сеть «многие ко многим» - всё это делается через PRIMARY KEY и FOREIGN KEY.
Связь 1:1 на практике почти не нужна. Обычно два таких объекта проще держать в одной таблице. В моей схеме её нет.
У одного клиента несколько машин. Одна машина проходит много заказов за свою жизнь. Один мастер ведёт много работ.
Напрямую в реляционной модели так нельзя. Нужна таблица-связка, в которой каждая запись это пара идентификаторов плюс доп. данные (количество, цена).
Пять обычных сценариев из работы автосервиса. Каждый превращается в короткий SQL-запрос. По сути, ради этого и собирается база.
SELECT c.brand, c.model, c.license_plate, c.mileage FROM cars c JOIN clients cl ON c.client_id = cl.client_id WHERE cl.last_name = 'Иванов';
SELECT o.order_id, cl.last_name, c.brand, c.license_plate, o.accept_date, o.total_cost FROM orders o JOIN cars c ON o.car_id = c.car_id JOIN clients cl ON c.client_id = cl.client_id WHERE MONTH(o.accept_date) = MONTH(CURRENT_DATE) AND YEAR(o.accept_date) = YEAR(CURRENT_DATE) ORDER BY o.accept_date DESC;
SELECT s.name, SUM(os.quantity * os.actual_price) AS revenue, COUNT(DISTINCT os.order_id) AS orders_count FROM order_services os JOIN services s ON os.service_id = s.service_id JOIN orders o ON os.order_id = o.order_id WHERE o.accept_date BETWEEN '2025-01-01' AND '2025-12-31' GROUP BY s.service_id, s.name ORDER BY revenue DESC;
-- сигнал к закупке, когда осталось меньше 5 штук SELECT part_id, name, article, stock_quantity, price FROM parts WHERE stock_quantity < 5 ORDER BY stock_quantity ASC;
SELECT e.last_name, e.first_name, COUNT(o.order_id) AS orders_done, SUM(o.total_cost) AS total_revenue FROM employees e JOIN orders o ON o.employee_id = e.employee_id WHERE o.complete_date IS NOT NULL AND o.complete_date BETWEEN '2025-01-01' AND '2025-12-31' GROUP BY e.employee_id, e.last_name, e.first_name ORDER BY orders_done DESC;