Основы проектирования схемы
Схема — это чертёж ваших данных: какие таблицы существуют, какие у них столбцы и как они связаны. Хорошее проектирование схемы — это в основном здравый смысл плюс несколько привычек.
- Одна таблица на «сущность». Пользователи, посты, заказы — каждое получает свою таблицу.
- Каждой строке нужен уникальный ID (первичный ключ).
- Связывайте таблицы внешними ключами, а не копированием данных туда-сюда.
- Выбирайте честные типы. Деньги — это
decimal, а неfloat. Даты — это временные метки, а не текст. - Не дублируйте. Если email пользователя живёт в пяти таблицах, в конце концов у вас будет пять разных email.
Вот простая схема для блога — такая, какую AI сгенерирует для вас:
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE posts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
author_id UUID NOT NULL REFERENCES users(id),
title TEXT NOT NULL,
body TEXT NOT NULL,
published BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_posts_author ON posts(author_id);
Обратите внимание на REFERENCES (внешний ключ, привязывающий пост к реальному пользователю) и ограничения NOT NULL и UNIQUE. Это страховочные барьеры, которые база данных обеспечивает за вас, так что плохие данные не смогут проскользнуть, даже когда в вашем коде есть баг.
Ограничение, которое стоит добавлять осознанно, — это что происходит, когда родитель исчезает. По умолчанию удаление пользователя, на которого всё ещё ссылаются его посты, завершится ошибкой — что часто и есть то, чего вы хотите. Но можно задать явно: REFERENCES users(id) ON DELETE CASCADE удалит и посты тоже, а ON DELETE RESTRICT заблокирует удаление. Выбрать намеренно лучше, чем узнать поведение по умолчанию болезненным путём в продакшене. Тот же инстинкт «пусть база данных защитит вас» применим к ограничению CHECK (CHECK (price >= 0)) — оно отвергает бессмысленные данные у источника, вместо того чтобы доверять валидации в каждом пути кода.