Перейти к содержимому

DDL (Data Definition Language)

DDL — язык определения структуры данных в реляционных БД. Команды: CREATE (создание объектов: TABLE, SCHEMA, INDEX, VIEW, DATABASE, SEQUENCE), ALTER (изменение структуры: ADD/DROP COLUMN, ALTER COLUMN, ADD/DROP CONSTRAINT, RENAME), DROP (удаление объектов: TABLE, INDEX, VIEW, SCHEMA, DATABASE), TRUNCATE (быстрая очистка всех строк, сброс автоинкремента), RENAME (переименование).

Введение: Архитектор базы данных

Представьте, что вы строите дом. Прежде чем заселяться, нужно заложить фундамент, возвести стены, проложить проводку, установить двери и окна. Только после этого можно заносить мебель и жить.

В мире баз данных то же самое. Прежде чем добавлять данные, нужно создать структуру для их хранения: таблицы, индексы, схемы, ограничения. Эту структуру создает DDL (Data Definition Language) — язык определения данных.

DDL — это набор команд, которые создают, изменяют и удаляют структуры базы данных. Они не работают с самими данными, а работают с “контейнерами” для данных.

КомандаНазначениеАналогия в строительстве
CREATEСоздать объект (таблицу, индекс, схему)Заложить фундамент, возвести стены
ALTERИзменить структуру объектаПристроить балкон, прорубить окно
DROPУдалить объектСнести дом
TRUNCATEОчистить таблицу (удалить все строки)Вынести всю мебель, но стены оставить
RENAMEПереименовать объектПовесить новую табличку на дверь

Важное отличие от DML (INSERT, UPDATE, DELETE): DDL изменяет структуру, DML изменяет данные. DDL обычно требует более высоких привилегий и часто не может быть откатан (не все DDL операции транзакционны).

CREATE: Создание структуры

CREATE TABLE

Самая важная DDL команда. Создает таблицу — контейнер для данных.

-- Простейшая таблица
CREATE TABLE users (
    id INTEGER,
    name TEXT,
    email TEXT
);

-- Таблица с типами данных и ограничениями
CREATE TABLE products (
    id SERIAL PRIMARY KEY,           -- автоинкремент, первичный ключ
    name VARCHAR(255) NOT NULL,      -- не может быть пустым
    price DECIMAL(10,2) CHECK (price >= 0),  -- проверочное ограничение
    category_id INTEGER REFERENCES categories(id),  -- внешний ключ
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    is_active BOOLEAN DEFAULT TRUE
);

Типы данных (основные):

ТипОписаниеПример
INTEGER, INTЦелое число42, -100
BIGINTБольшое целое (8 байт)9999999999
DECIMAL(p,s), NUMERICТочное десятичное число1234.56
REAL, FLOATПриблизительное число с плавающей точкой3.14159
VARCHAR(n)Строка переменной длины (макс n)“Иван”
TEXTСтрока неограниченной длиныдлинный текст…
DATEДата (без времени)‘2024-01-15’
TIMESTAMPДата и время‘2024-01-15 14:30:00’
BOOLEANЛогическое значениеTRUE, FALSE
JSON, JSONBJSON данные (PostgreSQL){"name": "Иван"}
UUIDУниверсальный уникальный идентификатор‘123e4567-e89b-12d3-a456-426614174000’

CREATE SCHEMA

Схема (schema) — это пространство имен для таблиц. Позволяет группировать таблицы по логическим модулям.

-- Создание схемы
CREATE SCHEMA sales;
CREATE SCHEMA hr;
CREATE SCHEMA analytics;

-- Создание таблицы в схеме
CREATE TABLE sales.orders (
    id SERIAL PRIMARY KEY,
    customer_id INTEGER,
    amount DECIMAL(10,2)
);

-- Переключение на схему (PostgreSQL)
SET search_path TO sales, public;

-- Создание схемы с владельцем
CREATE SCHEMA private AUTHORIZATION admin_role;

CREATE INDEX

Индекс ускоряет поиск данных. Будет подробно в других темах.

-- Обычный индекс (B-Tree)
CREATE INDEX idx_users_email ON users(email);

-- Уникальный индекс
CREATE UNIQUE INDEX idx_users_phone ON users(phone);

-- Составной индекс
CREATE INDEX idx_orders_customer_date ON orders(customer_id, created_at);

-- Частичный индекс (только для активных пользователей)
CREATE INDEX idx_users_active_email ON users(email) WHERE is_active = TRUE;

CREATE VIEW

Представление (view) — виртуальная таблица, основанная на запросе.

-- Создание представления
CREATE VIEW active_users AS
SELECT id, name, email FROM users WHERE is_active = TRUE;

-- Использование как таблицы
SELECT * FROM active_users WHERE email LIKE '%@company.com';

-- Материализованное представление (хранит данные)
CREATE MATERIALIZED VIEW daily_sales AS
SELECT DATE(created_at) as sale_date, SUM(amount) as total
FROM orders
GROUP BY DATE(created_at);

CREATE DATABASE

-- PostgreSQL
CREATE DATABASE myapp;

-- С кодировкой и владельцем
CREATE DATABASE myapp 
    ENCODING 'UTF8' 
    OWNER app_user;

-- MySQL
CREATE DATABASE myapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

CREATE SEQUENCE

Последовательность — генератор уникальных чисел.

-- Создание последовательности
CREATE SEQUENCE order_number_seq START 1000 INCREMENT 1;

-- Использование
SELECT nextval('order_number_seq');  -- 1000
SELECT nextval('order_number_seq');  -- 1001
SELECT currval('order_number_seq');  -- 1001

-- Привязка к столбцу
ALTER TABLE orders ALTER COLUMN order_number SET DEFAULT nextval('order_number_seq');

ALTER: Изменение структуры

ALTER TABLE (добавление колонок)

-- Добавление колонки
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- Добавление с значением по умолчанию
ALTER TABLE users ADD COLUMN status VARCHAR(20) DEFAULT 'active';

-- Добавление с NOT NULL (сначала добавить, потом заполнить, потом добавить NOT NULL)
ALTER TABLE users ADD COLUMN middle_name VARCHAR(100);
UPDATE users SET middle_name = '' WHERE middle_name IS NULL;
ALTER TABLE users ALTER COLUMN middle_name SET NOT NULL;

ALTER TABLE (изменение колонок)

-- Изменение типа данных
ALTER TABLE users ALTER COLUMN age TYPE SMALLINT;

-- Изменение имени колонки (PostgreSQL)
ALTER TABLE users RENAME COLUMN phone TO contact_phone;

-- Установка значения по умолчанию
ALTER TABLE users ALTER COLUMN status SET DEFAULT 'pending';

-- Удаление значения по умолчанию
ALTER TABLE users ALTER COLUMN status DROP DEFAULT;

-- Добавление NOT NULL
ALTER TABLE users ALTER COLUMN email SET NOT NULL;

-- Удаление NOT NULL
ALTER TABLE users ALTER COLUMN email DROP NOT NULL;

ALTER TABLE (ограничения)

-- Добавление первичного ключа
ALTER TABLE users ADD PRIMARY KEY (id);

-- Добавление внешнего ключа
ALTER TABLE orders ADD CONSTRAINT fk_orders_customer 
    FOREIGN KEY (customer_id) REFERENCES customers(id);

-- Добавление уникальности
ALTER TABLE users ADD CONSTRAINT uk_users_email UNIQUE (email);

-- Добавление проверочного ограничения
ALTER TABLE products ADD CONSTRAINT chk_products_price 
    CHECK (price >= 0);

-- Удаление ограничения
ALTER TABLE users DROP CONSTRAINT uk_users_email;

ALTER TABLE (удаление колонок)

-- Удаление колонки (осторожно!)
ALTER TABLE users DROP COLUMN middle_name;

-- Удаление с проверкой зависимости (CASCADE)
ALTER TABLE users DROP COLUMN department_id CASCADE;

ALTER SCHEMA / ALTER INDEX

-- Переименование схемы
ALTER SCHEMA sales RENAME TO ecommerce;

-- Переименование индекса
ALTER INDEX idx_users_email RENAME TO idx_users_email_lower;

-- Изменение владельца
ALTER TABLE users OWNER TO new_admin;

DROP: Удаление структуры

DROP TABLE

-- Удаление таблицы (безвозвратно!)
DROP TABLE users;

-- Удаление с проверкой существования (без ошибки, если нет)
DROP TABLE IF EXISTS users;

-- Каскадное удаление (удаляет зависимые объекты)
DROP TABLE departments CASCADE;

Важно: DROP TABLE удаляет и данные, и структуру. Операция часто не может быть откатана (не транзакционна в некоторых СУБД).

DROP INDEX

-- Удаление индекса
DROP INDEX idx_users_email;

-- Без ошибки, если нет
DROP INDEX IF EXISTS idx_users_phone;

DROP VIEW

-- Удаление представления
DROP VIEW active_users;

-- Каскадное удаление (удаляет представления, зависящие от этого)
DROP VIEW active_users CASCADE;

DROP SCHEMA

-- Удаление пустой схемы
DROP SCHEMA old_schema;

-- Удаление схемы со всем содержимым
DROP SCHEMA old_schema CASCADE;

DROP DATABASE

-- Удаление базы данных (PostgreSQL)
DROP DATABASE myapp;

-- MySQL
DROP DATABASE myapp;

TRUNCATE: Быстрая очистка

TRUNCATE удаляет все строки из таблицы, но сохраняет структуру. Это намного быстрее, чем DELETE FROM table, потому что не логирует каждую строку.

-- Очистка таблицы (быстро, не логирует строки)
TRUNCATE TABLE logs;

-- Очистка с переиспользованием места
TRUNCATE TABLE temp_data;

-- Каскадная очистка (очищает связанные таблицы)
TRUNCATE TABLE departments CASCADE;

-- Перезапуск последовательностей (сброс автоинкремента)
TRUNCATE TABLE users RESTART IDENTITY;

TRUNCATE vs DELETE:

ХарактеристикаTRUNCATEDELETE
СкоростьОчень быстраяМедленная (построчная)
ЛогированиеМинимальноеКаждая строка
ТриггерыНе срабатываютСрабатывают
Условие WHEREНе поддерживаетсяПоддерживается
Сброс автоинкрементаДа (обычно)Нет
Откат (ROLLBACK)Да (в транзакции)Да
БлокировкаВысокий уровеньНизкий уровень

RENAME: Переименование

-- Переименование таблицы
ALTER TABLE users RENAME TO app_users;

-- Переименование колонки
ALTER TABLE users RENAME COLUMN phone TO mobile;

-- Переименование индекса
ALTER INDEX idx_users_email RENAME TO idx_app_users_email;

Ограничения (Constraints)

Ограничения — это правила целостности данных. Они обеспечивают, чтобы “плохие” данные не попали в таблицу.

PRIMARY KEY

Уникальный идентификатор строки. Комбинация UNIQUE + NOT NULL.

-- Одноколоночный
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT
);

-- Составной (несколько колонок)
CREATE TABLE order_items (
    order_id INTEGER,
    product_id INTEGER,
    quantity INTEGER,
    PRIMARY KEY (order_id, product_id)
);

FOREIGN KEY

Ссылка на первичный ключ другой таблицы. Обеспечивает ссылочную целостность.

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    customer_id INTEGER REFERENCES customers(id),
    amount DECIMAL(10,2)
);

-- С каскадным удалением
CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    customer_id INTEGER REFERENCES customers(id) ON DELETE CASCADE,
    amount DECIMAL(10,2)
);

-- ON DELETE варианты
-- NO ACTION (по умолчанию) — запретить удаление
-- CASCADE — удалить связанные записи
-- SET NULL — установить NULL
-- SET DEFAULT — установить значение по умолчанию
-- RESTRICT — запретить удаление (немного отличается от NO ACTION)

UNIQUE

Запрещает дублирование значений в колонке (или комбинации колонок).

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE,
    phone VARCHAR(20)
);

-- Составной UNIQUE
CREATE TABLE user_roles (
    user_id INTEGER,
    role_id INTEGER,
    UNIQUE (user_id, role_id)
);

CHECK

Проверочное ограничение. Значение должно удовлетворять условию.

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    price DECIMAL(10,2) CHECK (price >= 0),
    age_restriction INTEGER CHECK (age_restriction IN (0, 6, 12, 16, 18)),
    discount DECIMAL(5,2) CHECK (discount BETWEEN 0 AND 100)
);

-- CHECK с несколькими колонками
CREATE TABLE events (
    start_date DATE,
    end_date DATE,
    CHECK (end_date > start_date)
);

NOT NULL

Запрещает пустые значения.

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT NOT NULL
);

DEFAULT

Значение по умолчанию, если не указано.

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    status VARCHAR(20) DEFAULT 'pending',
    is_deleted BOOLEAN DEFAULT FALSE
);

Типы данных в деталях

Числовые типы

ТипРазмерДиапазонКогда использовать
SMALLINT2 байта-32768 до 32767Маленькие числа (возраст, количество)
INTEGER4 байта-2.1 млрд до 2.1 млрдСтандартный выбор для ID
BIGINT8 байтов-9.2 квинтиллионов до 9.2 квинтиллионовБольшие таблицы, счетчики
DECIMAL(10,2)ПеременныйДо 131072 цифрДеньги, точные расчеты
REAL4 байта~6-7 цифр точностиНаучные расчеты, геоданные
DOUBLE8 байтов~15-16 цифр точностиБолее точные научные расчеты

Строковые типы

ТипОписаниеКогда использовать
CHAR(n)Фиксированная длина (дополняется пробелами)Коды, фиксированные форматы (ISO коды стран)
VARCHAR(n)Переменная длина с максимумомИмена, адреса, email (99% случаев)
TEXTНеограниченная длинаДлинные тексты, описания, статьи

Дата и время

ТипОписаниеПример
DATEТолько дата‘2024-01-15’
TIMEТолько время‘14:30:00’
TIMESTAMPДата и время‘2024-01-15 14:30:00’
TIMESTAMPTZДата и время с часовым поясом‘2024-01-15 14:30:00+03’
INTERVALПромежуток времени‘3 days’, ‘2 hours’

Специальные типы

-- JSON/JSONB (PostgreSQL)
CREATE TABLE events (
    id SERIAL PRIMARY KEY,
    payload JSONB
);
INSERT INTO events (payload) VALUES ('{"user_id": 123, "action": "login"}');

-- UUID
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name TEXT
);

-- Массивы (PostgreSQL)
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    tags TEXT[]
);
INSERT INTO products (tags) VALUES (ARRAY['electronics', 'sale']);

-- Перечисления (ENUM)
CREATE TYPE user_status AS ENUM ('active', 'pending', 'blocked');
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    status user_status
);

Временные таблицы

Временные таблицы существуют только в течение сессии или транзакции.

-- Временная таблица (удаляется при закрытии сессии)
CREATE TEMP TABLE temp_orders AS
SELECT * FROM orders WHERE created_at > CURRENT_DATE - INTERVAL '30 days';

-- Временная таблица с явной структурой
CREATE TEMP TABLE temp_calc (
    id INTEGER,
    value DECIMAL(10,2)
);

-- Локальная временная таблица (только для текущей сессии)
CREATE LOCAL TEMPORARY TABLE local_data (id INT) ON COMMIT DROP;

Схемы и пространства имен

-- Создание схемы
CREATE SCHEMA app;
CREATE SCHEMA app_private;
CREATE SCHEMA audit;

-- Создание таблицы в схеме
CREATE TABLE app.users (id INT, name TEXT);

-- Поиск таблицы в схеме (search_path)
SHOW search_path;  -- "$user", public
SET search_path TO app, public;

-- Удаление схемы
DROP SCHEMA app CASCADE;

DDL и транзакции

Важное отличие: в разных СУБД DDL по-разному работает с транзакциями.

PostgreSQL (транзакционный DDL)

В PostgreSQL почти все DDL операции транзакционны. Можно сделать ROLLBACK после CREATE TABLE.

BEGIN;
CREATE TABLE test (id INT);
INSERT INTO test VALUES (1);
ROLLBACK;  -- Таблица test исчезнет

MySQL (ограниченно транзакционный)

В MySQL с InnoDB некоторые DDL транзакционны, но не все.

START TRANSACTION;
CREATE TABLE test (id INT);  -- CREATE TABLE транзакционна
ALTER TABLE test ADD COLUMN name TEXT;  -- ALTER не транзакционна
ROLLBACK;  -- Таблица удалится, но ALTER уже был выполнен

Oracle (нетранзакционный DDL)

В Oracle DDL не транзакционен. Он неявно делает COMMIT перед выполнением.

BEGIN
    INSERT INTO users VALUES (1);  -- DML
    CREATE TABLE test (id INT);     -- DDL: неявный COMMIT
    ROLLBACK;                       -- Откатит только после CREATE, но не INSERT
END;

Распространенные ошибки

Ошибка 1: DROP без IF EXISTS

-- Плохо (вызовет ошибку, если таблицы нет)
DROP TABLE users;

-- Хорошо
DROP TABLE IF EXISTS users;

Ошибка 2: Неправильный порядок FOREIGN KEY

-- Плохо: ссылается на таблицу, которая еще не создана
CREATE TABLE orders (
    id INT PRIMARY KEY,
    customer_id INT REFERENCES customers(id)  -- customers не существует
);

-- Хорошо: сначала customers, потом orders
CREATE TABLE customers (id INT PRIMARY KEY);
CREATE TABLE orders (id INT PRIMARY KEY, customer_id INT REFERENCES customers(id));

Ошибка 3: Слишком большие VARCHAR

-- Плохо (VARCHAR(1000) для имени)
name VARCHAR(1000)

-- Хорошо (реалистичный лимит)
name VARCHAR(100)

Ошибка 4: NOT NULL без DEFAULT

-- Плохо: добавить NOT NULL в существующую таблицу с данными
ALTER TABLE users ADD COLUMN middle_name TEXT NOT NULL;  -- Ошибка!

-- Хорошо: сначала добавить с DEFAULT, потом убрать
ALTER TABLE users ADD COLUMN middle_name TEXT DEFAULT '';
UPDATE users SET middle_name = '' WHERE middle_name IS NULL;
ALTER TABLE users ALTER COLUMN middle_name SET NOT NULL;
ALTER TABLE users ALTER COLUMN middle_name DROP DEFAULT;

Ошибка 5: Каскадное удаление без понимания последствий

-- Опасно: удалит все заказы при удалении пользователя
ALTER TABLE orders ADD CONSTRAINT fk_orders_user 
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

-- Безопаснее: запретить удаление пользователя с заказами
ALTER TABLE orders ADD CONSTRAINT fk_orders_user 
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT;

Резюме для системного аналитика

  1. DDL (Data Definition Language) — язык определения структуры данных. Создает, изменяет и удаляет таблицы, индексы, схемы, ограничения. Не работает с самими данными, работает с “контейнерами”.

  2. Основные команды: CREATE (создать), ALTER (изменить), DROP (удалить), TRUNCATE (очистить), RENAME (переименовать).

  3. Типы данных — критически важны. INTEGER для ID, VARCHAR для строк, DECIMAL для денег, TIMESTAMP для дат. Неправильный выбор типа ведет к проблемам с производительностью и точностью.

  4. Ограничения (Constraints) — стражи целостности данных. PRIMARY KEY (уникальный идентификатор), FOREIGN KEY (ссылка на другую таблицу), UNIQUE (уникальность), CHECK (проверка условия), NOT NULL (обязательное значение).

  5. TRUNCATE vs DELETE: TRUNCATE — быстрая очистка всей таблицы, не логирует строки, сбрасывает автоинкремент. DELETE — построчное удаление с условием, медленнее, логируется.

  6. Транзакционность DDL зависит от СУБД. PostgreSQL поддерживает транзакционный DDL (можно ROLLBACK). MySQL — ограниченно. Oracle — не поддерживает (неявный COMMIT).

Проверка знаний

Вопрос 1 из 4
Что относится к DDL?
Для чего нужен DDL?
Что делает ALTER TABLE?
Почему DDL требует осторожности?

Вопросы, где были ошибки