DDL (Data Definition Language) — команды для управления структурой базы данных: создание, изменение и удаление таблиц, индексов, представлений. В отличие от DML, DDL меняет схему, а не данные.
CREATE TABLE — создание таблицы
Базовый синтаксис
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
description TEXT,
price NUMERIC(10, 2) NOT NULL,
stock INTEGER DEFAULT 0,
category_id INTEGER,
created_at TIMESTAMPTZ DEFAULT NOW()
);
Типы данных (часто используемые)
| Тип | Описание | Пример |
|---|---|---|
INTEGER / INT | Целое число (-2B до 2B) | id, количество |
BIGINT | Большое целое (-9E18 до 9E18) | user_id у крупных систем |
SERIAL | Автоинкремент (int) | Первичные ключи |
BIGSERIAL | Автоинкремент (bigint) | — |
NUMERIC(p, s) | Точное число p знаков, s после запятой | Деньги |
FLOAT / DOUBLE PRECISION | Приближённое число | Координаты, вес |
VARCHAR(n) | Строка до n символов | Имена, email |
TEXT | Строка без ограничений | Описания, контент |
BOOLEAN | true/false | Флаги |
DATE | Дата без времени | Дни рождения |
TIMESTAMP | Дата+время без tz | |
TIMESTAMPTZ | Дата+время с tz | Рекомендуется |
UUID | UUID v4 | Глобальные ID |
JSONB | JSON (бинарный) | Гибкие данные |
Ограничения (Constraints)
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
amount NUMERIC(10, 2) NOT NULL CHECK (amount > 0),
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'completed', 'refunded')),
coupon_code VARCHAR(20) UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Внешний ключ
CONSTRAINT fk_orders_user
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
| Ограничение | Что делает |
|---|---|
NOT NULL | Запрещает NULL |
UNIQUE | Уникальность значения |
PRIMARY KEY | NOT NULL + UNIQUE (один на таблицу) |
CHECK (expr) | Произвольное условие |
FOREIGN KEY | Ссылочная целостность |
DEFAULT value | Значение по умолчанию |
ON DELETE / ON UPDATE для внешних ключей
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE -- удалить заказы при удалении пользователя
ON DELETE SET NULL -- user_id = NULL при удалении пользователя
ON DELETE RESTRICT -- запретить удаление пользователя, если есть заказы
ON DELETE NO ACTION -- то же что RESTRICT, проверяется в конце транзакции
IF NOT EXISTS
CREATE TABLE IF NOT EXISTS categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
-- Не выдаёт ошибку, если таблица уже существует
ALTER TABLE — изменение структуры
Добавить столбец
ALTER TABLE products
ADD COLUMN weight_kg NUMERIC(6, 2);
-- С умолчанием
ALTER TABLE products
ADD COLUMN is_featured BOOLEAN NOT NULL DEFAULT false;
В PostgreSQL 11+
ADD COLUMN ... DEFAULT constantработает мгновенно (не переписывает таблицу). Изменяемые DEFAULT (NOW()) требуют перезаписи.
Удалить столбец
ALTER TABLE products
DROP COLUMN description;
-- Если есть зависимые объекты (views, foreign keys):
ALTER TABLE products
DROP COLUMN old_category CASCADE;
Изменить тип столбца
ALTER TABLE products
ALTER COLUMN price TYPE BIGINT;
-- С явным приведением
ALTER TABLE events
ALTER COLUMN metadata TYPE JSONB USING metadata::jsonb;
Переименовать столбец
ALTER TABLE users
RENAME COLUMN full_name TO display_name;
Переименовать таблицу
ALTER TABLE user_data RENAME TO users;
Добавить/удалить ограничения
-- Добавить NOT NULL
ALTER TABLE products
ALTER COLUMN name SET NOT NULL;
-- Убрать NOT NULL
ALTER TABLE products
ALTER COLUMN description DROP NOT NULL;
-- Добавить CHECK
ALTER TABLE products
ADD CONSTRAINT chk_price_positive CHECK (price > 0);
-- Убрать ограничение
ALTER TABLE products
DROP CONSTRAINT chk_price_positive;
-- Добавить UNIQUE
ALTER TABLE users
ADD CONSTRAINT uq_users_email UNIQUE (email);
-- Добавить внешний ключ
ALTER TABLE orders
ADD CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id);
Изменить DEFAULT
ALTER TABLE orders
ALTER COLUMN status SET DEFAULT 'pending';
ALTER TABLE orders
ALTER COLUMN status DROP DEFAULT;
DROP — удаление объектов
-- Удалить таблицу
DROP TABLE products;
-- Если существует
DROP TABLE IF EXISTS temp_data;
-- С зависимостями (views, foreign keys)
DROP TABLE categories CASCADE;
-- Удалить несколько таблиц
DROP TABLE IF EXISTS sessions, logs, temp_imports;
⚠️
DROP TABLEудаляет данные необратимо. Всегда делайте бэкап или переименовывайте перед удалением.
Безопасные изменения схемы в продакшне
Изменения схемы на нагруженной базе могут блокировать таблицу. Вот безопасные подходы:
Добавление индекса без блокировки
-- Обычный CREATE INDEX блокирует таблицу на время создания
CREATE INDEX idx_orders_user_id ON orders(user_id);
-- CONCURRENTLY — не блокирует, но дольше и не работает в транзакции
CREATE INDEX CONCURRENTLY idx_orders_user_id ON orders(user_id);
NOT VALID для внешних ключей
-- Добавить FK без проверки существующих строк (быстро)
ALTER TABLE orders
ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id)
NOT VALID;
-- Проверить в фоне (без блокировки INSERT/UPDATE)
ALTER TABLE orders VALIDATE CONSTRAINT fk_user;
Паттерн для переименования столбца
Переименование ломает существующий код. Безопасно:
- Добавить новый столбец
- Скопировать данные
- Добавить триггер синхронизации
- Обновить код приложения
- Удалить старый столбец
-- Шаг 1: добавить новый столбец
ALTER TABLE users ADD COLUMN display_name TEXT;
-- Шаг 2: скопировать данные
UPDATE users SET display_name = full_name;
-- Шаг 3: в приложении переключиться на display_name
-- Шаг 4: удалить старый
ALTER TABLE users DROP COLUMN full_name;
Схемы в PostgreSQL
Таблицы живут в схемах (namespaces):
-- Создать схему
CREATE SCHEMA analytics;
-- Создать таблицу в схеме
CREATE TABLE analytics.events (
id BIGSERIAL PRIMARY KEY,
event_type VARCHAR(50),
payload JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Путь поиска схем
SET search_path = analytics, public;
Временные таблицы
-- Живут только в текущей сессии
CREATE TEMP TABLE temp_results AS
SELECT user_id, SUM(amount) AS total
FROM orders
GROUP BY user_id;
-- Использовать как обычную таблицу
SELECT * FROM temp_results WHERE total > 10000;
-- Автоматически удаляется при закрытии сессии
Итог
| Команда | Что делает |
|---|---|
CREATE TABLE | Создать таблицу с ограничениями |
ALTER TABLE ADD COLUMN | Добавить столбец |
ALTER TABLE DROP COLUMN | Удалить столбец |
ALTER TABLE ALTER COLUMN TYPE | Изменить тип |
ALTER TABLE RENAME COLUMN | Переименовать столбец |
ALTER TABLE ADD CONSTRAINT | Добавить ограничение |
DROP TABLE | Удалить таблицу |
CREATE INDEX CONCURRENTLY | Индекс без блокировки |
DDL в продакшне требует осторожности — блокировки, зависимости, откаты. Используйте инструменты миграций (Liquibase, Flyway, Alembic) для управления изменениями схемы.