Проверка SQL-схемы: что в ней не так
Ниже — не выдуманный список, а настоящий отчёт проверки для схемы слева. Те же находки вы увидите в редакторе на вкладке «Проверки», и часть из них там чинится кнопкой.
CREATE TABLE user (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL,
balance FLOAT,
created_at TIMESTAMP NOT NULL
);
CREATE TABLE orders (
order_id INTEGER NOT NULL,
user_id INTEGER REFERENCES user (id),
total FLOAT NOT NULL,
placedAt TIMESTAMP
);- Определилось как
PostgreSQL
Скрипт остаётся в браузере: и разбор, и генерация идут на вашей машине, на сервер ничего не уходит.
Что нашлось
Стороны связи разного типа
orders.user_idКолонка и то, на что она ссылается, объявлены по-разному. Одни базы откажут сразу, другие будут молча приводить типы при каждом соединении.
Таблица без первичного ключа
ordersСтроку такой таблицы нельзя надёжно адресовать: ни обновить одну из двух одинаковых, ни сослаться на неё. Репликация и ORM тоже рассчитывают на ключ.
Внешний ключ без индекса
ordersПо этой колонке идут соединения и проверки при удалении родителя. Без индекса каждая из них — полный проход по таблице.
Внешний ключ допускает NULL
orders.user_idИногда так и задумано: связь необязательна. Но чаще NOT NULL просто забыли, и в таблице заводятся строки без родителя.
Имя — зарезервированное слово
userТакое имя придётся в каждом запросе брать в кавычки. Переименовать дешевле, чем помнить об исключении.
Деньги в приблизительном типе
user.balanceorders.totalFLOAT и REAL хранят число приближённо: суммы перестают сходиться после первого же деления. Для денег нужен NUMERIC или DECIMAL.
TIMESTAMP без часового пояса
user.created_atorders.placedAtВ PostgreSQL такой столбец пояса не хранит и молча читается как местное время. При переезде сервера или переходе на летнее время значения разъезжаются — обычно имеется в виду timestamptz.
Разный стиль имён
orders.placedAtВ схеме соседствуют snake_case и camelCase. Перечислены имена в стиле меньшинства: привести их к общему проще, чем помнить исключения.
Не задано ON DELETE
ordersЧто делать с потомками при удалении родителя, решает база — по умолчанию она удаление запретит. Если это не то, что нужно, лучше написать явно.