Добавить информацию в таблицу MySQL можно двумя разными командами. INSERT добавляет новые строки (записи) в существующие столбцы. ALTER TABLE … ADD COLUMN добавляет новый столбец, а его параметры — тип, NULL/NOT NULL, DEFAULT, CHECK — задаются прямо в определении столбца. Изменить параметр уже существующего столбца можно через ALTER TABLE ... ALTER COLUMN (значение по умолчанию и видимость столбца) или MODIFY COLUMN (полное определение). UPDATE ничего не добавляет — он меняет строки, которые уже есть.
Содержание
- Учебный стенд
- INSERT: добавить одну строку
- Несколько строк одним запросом
- INSERT … SET и INSERT … SELECT
- Ошибки NOT NULL, DEFAULT и длины
- Дубликаты: IGNORE, ON DUPLICATE KEY UPDATE, REPLACE
- ALTER TABLE ADD COLUMN: добавить столбец с параметрами
- Изменить параметр существующего столбца
- ALTER TABLE на рабочей базе
- Выводы
- Где применяется / связь с практикой
- FAQ
Ниже — учебный стенд, вставка одной и нескольких строк, INSERT ... SELECT, дубликаты, типовые ошибки NOT NULL/DEFAULT и безопасное добавление столбца. Все примеры и тексты ошибок получены в MySQL 8.4.11 (ветка LTS) 24.09.2026.
| Задача | Команда | Что меняется |
|---|---|---|
| Добавить строку | INSERT INTO t (a, b) VALUES (...) |
данные, структура та же |
| Добавить столбец | ALTER TABLE t ADD COLUMN c INT NOT NULL DEFAULT 0 |
структура таблицы |
| Поменять или убрать значение по умолчанию | ALTER TABLE t ALTER COLUMN c SET DEFAULT 5 (DROP DEFAULT) |
только DEFAULT; та же форма меняет видимость: SET INVISIBLE / SET VISIBLE |
| Поменять тип, NULL/NOT NULL | ALTER TABLE t MODIFY COLUMN c ... |
определение столбца целиком |
| Изменить существующие строки | UPDATE t SET ... |
данные, новых строк нет |
Учебный стенд
Самый быстрый способ получить чистый MySQL — официальный образ Docker. Пароль ниже учебный, для рабочего сервера он не годится.
docker run --rm -d --name mysql84 -e MYSQL_ROOT_PASSWORD=learn -e MYSQL_DATABASE=shop mysql:8.4
docker exec -it mysql84 mysql -uroot -p shop
Таблица товаров, на которой построены все примеры:
CREATE TABLE products (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
sku VARCHAR(20) NOT NULL UNIQUE,
title VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL DEFAULT 0
);
Здесь у каждого столбца уже есть параметры: id заполняется автоматически, sku уникален, title и price обязательны, у stock есть значение по умолчанию.
INSERT: добавить одну строку
INSERT INTO products (sku, title, price)
VALUES ('KB-01', 'Клавиатура', 2490.00);
SELECT LAST_INSERT_ID();
SELECT * FROM products;
LAST_INSERT_ID() вернет 1, а таблица будет такой:
+----+-------+----------------------+---------+-------+
| id | sku | title | price | stock |
+----+-------+----------------------+---------+-------+
| 1 | KB-01 | Клавиатура | 2490.00 | 0 |
+----+-------+----------------------+---------+-------+
Столбцы id и stock не перечислены, поэтому MySQL подставил автоинкремент и DEFAULT 0. Список столбцов стоит писать всегда: запрос без него (INSERT INTO products VALUES (...)) требует значения для каждого столбца в порядке CREATE TABLE и ломается после первого же ADD COLUMN — это показано ниже.
Несколько строк одним запросом
INSERT INTO products (sku, title, price, stock) VALUES
('MS-01', 'Мышь', 990.00, 15),
('HS-01', 'Гарнитура', 3990.00, 4),
('CB-01', 'Кабель USB-C', 390.00, DEFAULT);
SELECT LAST_INSERT_ID();
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
Ключевое слово DEFAULT в списке значений явно просит значение по умолчанию. LAST_INSERT_ID() после пачки вернет 2 — id первой добавленной строки, а не последней.
Пачка в InnoDB атомарна: если одна строка нарушает ограничение, не добавится ни одна.
INSERT INTO products (sku, title, price) VALUES
('PD-01', 'Коврик', 450.00),
('WB-01', NULL, 1990.00);
SELECT COUNT(*) FROM products;
ERROR 1048 (23000): Column 'title' cannot be null
COUNT(*) вернет 4: коврик тоже не попал в таблицу. При этом значения автоинкремента, выделенные под неудачную вставку, обычно не возвращаются, поэтому в id появляются пропуски. Это нормально: id гарантирует уникальность, а не непрерывность.
INSERT … SET и INSERT … SELECT
В MySQL есть форма со SET, похожая на UPDATE. Она удобна для одной строки с большим числом столбцов, но это расширение MySQL, и несколько строк так не вставить.
INSERT INTO products SET sku = 'PD-01', title = 'Коврик', price = 450.00;
INSERT ... SELECT копирует строки из другой таблицы или результата запроса. Типичный сценарий — загрузка прайса во временную таблицу и перенос только новых товаров:
CREATE TABLE price_import (
sku VARCHAR(20) NOT NULL,
title VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL
);
INSERT INTO price_import (sku, title, price) VALUES
('KB-01', 'Клавиатура', 2590.00),
('WC-01', 'Веб-камера', 1990.00),
('SP-01', 'Колонка', 2990.00);
INSERT INTO products (sku, title, price)
SELECT i.sku, i.title, i.price
FROM price_import AS i
LEFT JOIN products AS p ON p.sku = i.sku
WHERE p.id IS NULL;
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
Добавились веб-камера и колонка, клавиатура уже была. Столбцы сопоставляются по порядку в SELECT, а не по именам, поэтому порядок в обоих списках должен совпадать.
Ошибки NOT NULL, DEFAULT и длины
В MySQL 8.4 по умолчанию включен строгий режим (STRICT_TRANS_TABLES в @@sql_mode), и неверная вставка падает с ошибкой.
Неверно — пропущен обязательный столбец без значения по умолчанию:
INSERT INTO products (sku, price) VALUES ('WC-02', 1990.00);
ERROR 1364 (HY000): Field 'title' doesn't have a default value
Явный NULL в обязательный столбец дает другую ошибку, а слишком длинная строка — третью:
INSERT INTO products (sku, title, price) VALUES ('WC-02', NULL, 1990.00);
INSERT INTO products (sku, title, price)
VALUES ('WEBCAM-FULLHD-2026-USB', 'Веб-камера', 1990.00);
ERROR 1048 (23000): Column 'title' cannot be null
ERROR 1406 (22001): Data too long for column 'sku' at row 1
Исправление — передать значение нужной длины или, если пропуск допустим по смыслу, дать столбцу DEFAULT (раздел ниже):
INSERT INTO products (sku, title, price) VALUES ('WC-02', 'Веб-камера HD', 1990.00);
Без строгого режима (или со словом IGNORE) те же вставки проходят с предупреждением: вместо ошибки в title окажется пустая строка, длинный sku обрежется. Проверить режим можно запросом SELECT @@sql_mode;.
Дубликаты: IGNORE, ON DUPLICATE KEY UPDATE, REPLACE
Повторный sku нарушает UNIQUE:
INSERT INTO products (sku, title, price) VALUES ('MS-01', 'Мышь', 1090.00);
ERROR 1062 (23000): Duplicate entry 'MS-01' for key 'products.sku'
| Вариант | Что происходит при дубликате | Риск |
|---|---|---|
INSERT IGNORE |
строка пропускается, ошибка становится предупреждением | глушит и другие ошибки, в том числе NOT NULL |
ON DUPLICATE KEY UPDATE |
существующая строка обновляется | нужно явно указать, какие поля менять |
REPLACE |
старая строка удаляется, вставляется новая | меняется id, неуказанные поля сбрасываются в DEFAULT |
Для «добавить или обновить» обычно берут ON DUPLICATE KEY UPDATE. С MySQL 8.0.19 новые значения доступны через псевдоним строки. Неверно — неполное имя столбца:
INSERT INTO products (sku, title, price, stock)
VALUES ('MS-01', 'Мышь', 1090.00, 5) AS new
ON DUPLICATE KEY UPDATE price = new.price, stock = stock + new.stock;
ERROR 1052 (23000): Column 'stock' in field list is ambiguous
Исправление — указать, что stock берется из таблицы:
INSERT INTO products (sku, title, price, stock)
VALUES ('MS-01', 'Мышь', 1090.00, 5) AS new
ON DUPLICATE KEY UPDATE price = new.price, stock = products.stock + new.stock;
SELECT sku, price, stock FROM products WHERE sku = 'MS-01';
Query OK, 2 rows affected (0.00 sec)
+-------+---------+-------+
| sku | price | stock |
+-------+---------+-------+
| MS-01 | 1090.00 | 20 |
+-------+---------+-------+
Число затронутых строк здесь значит: 1 — вставлена новая, 2 — обновлена существующая, 0 — обновлять было нечего (значения совпали). Старая запись VALUES(price) еще работает, но выдает предупреждение 1287 об устаревании.
INSERT IGNORE опаснее, чем кажется. Он пропустит не только дубликат, но и строку без обязательного поля:
INSERT IGNORE INTO products (sku, price) VALUES ('NT-01', 500.00);
SHOW WARNINGS;
Query OK, 1 row affected, 1 warning (0.00 sec)
+---------+------+--------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------+
| Warning | 1364 | Field 'title' doesn't have a default value |
+---------+------+--------------------------------------------+
Строка добавлена с пустым title, хотя столбец объявлен NOT NULL.
ALTER TABLE ADD COLUMN: добавить столбец с параметрами
Параметры нового столбца пишутся так же, как в CREATE TABLE. AFTER задает позицию, ALGORITHM = INSTANT просит изменить только метаданные без перестройки таблицы (в MySQL 8.4 это поддерживается для добавления столбца в любую позицию).
ALTER TABLE products
ADD COLUMN weight_g INT UNSIGNED NULL AFTER price,
ADD COLUMN is_active TINYINT NOT NULL DEFAULT 1,
ALGORITHM = INSTANT;
ALTER TABLE products
ADD CONSTRAINT chk_products_active CHECK (is_active IN (0, 1));
INSERT INTO products (sku, title, price, is_active) VALUES ('BAD-01', 'Тест', 1, 5);
ERROR 3819 (HY000): Check constraint 'chk_products_active' is violated.
У существующих строк weight_g стал NULL, а is_active — 1 из DEFAULT. Флаг ограничен CHECK, и отрицательный тест это подтверждает. Добавление CHECK уже не мгновенное: с ALGORITHM = INSTANT или INPLACE MySQL отвечает ошибкой 1845 и предлагает ALGORITHM=COPY, то есть копирование таблицы.
Неочевидная ловушка — NOT NULL без DEFAULT на непустой таблице. Ошибки не будет:
ALTER TABLE products ADD COLUMN brand VARCHAR(50) NOT NULL;
SELECT sku, CONCAT('[', brand, ']') AS brand FROM products ORDER BY id LIMIT 2;
+-------+-------+
| sku | brand |
+-------+-------+
| KB-01 | [] |
| MS-01 | [] |
+-------+-------+
MySQL заполнил существующие строки неявным значением типа — пустой строкой (для чисел это 0). Формально ограничение соблюдено, по смыслу данных нет. Правильно — сразу задать осмысленное значение по умолчанию (на учебной таблице неудачный столбец просто удаляем):
ALTER TABLE products DROP COLUMN brand;
ALTER TABLE products ADD COLUMN brand VARCHAR(50) NOT NULL DEFAULT 'no-name';
Если значения по умолчанию нет, надежнее три шага: добавить столбец как NULL, заполнить его через UPDATE, затем сделать NOT NULL через MODIFY.
Новый столбец ломает вставку без списка столбцов — обещанная выше демонстрация:
INSERT INTO products VALUES (NULL, 'Q-01', 'Товар', 100.00, 0);
ERROR 1136 (21S01): Column count doesn't match value count at row 1
Изменить параметр существующего столбца
Для значения по умолчанию есть короткая форма (SET DEFAULT, DROP DEFAULT), остальное определение она не трогает:
ALTER TABLE products ALTER COLUMN stock SET DEFAULT 1;
MODIFY COLUMN заменяет определение целиком. Неверно — расширить title, забыв повторить NOT NULL:
ALTER TABLE products MODIFY COLUMN title VARCHAR(150);
INSERT INTO products (sku, price) VALUES ('ZZ-01', 10.00);
Вставка без title проходит: SHOW CREATE TABLE products теперь показывает title varchar(150) DEFAULT NULL, ограничение пропало молча. Вернуть его сразу не выйдет:
ALTER TABLE products MODIFY COLUMN title VARCHAR(150) NOT NULL;
ERROR 1138 (22004): Invalid use of NULL value
Исправление — сначала исправить данные, потом повторить полное определение:
UPDATE products SET title = 'Без названия' WHERE title IS NULL OR title = '';
ALTER TABLE products MODIFY COLUMN title VARCHAR(150) NOT NULL;
UPDATE затронет две строки: ZZ-01 с NULL и NT-01 с пустой строкой после INSERT IGNORE. Перед любым MODIFY сверяйтесь с SHOW CREATE TABLE и переносите в команду все параметры столбца: NOT NULL, DEFAULT, COMMENT, кодировку.
ALTER TABLE на рабочей базе
INSERT можно выполнить в транзакции и откатить ROLLBACK. С ALTER TABLE так не получится: команда неявно завершает текущую транзакцию. Поэтому порядок такой:
- Сделать резервную копию таблицы и проверить, что она восстанавливается.
- Прогнать ту же команду на копии с похожим объемом данных.
- Указать
ALGORITHM = INSTANTилиINPLACEявно: если операция требует копирования таблицы, MySQL откажет сразу, а не начнет долгую перестройку. - Проверить долгие транзакции:
ALTERждет блокировку метаданных, а пока ждет, за ним встают обычные запросы к таблице. - Заранее написать откат (
DROP COLUMNили обратныйMODIFY) и проверить приложение, которое читает таблицу.
Из кода приложения значения в INSERT передают только параметрами драйвера (? или именованными), не склейкой строк: это защита от SQL-инъекций, а не вопрос удобства.
Выводы
- Строки добавляет
INSERT, столбцы —ALTER TABLE ... ADD COLUMN; параметры столбца задаются в его определении. - Список столбцов в
INSERTписать всегда; пачка строк в InnoDB вставляется целиком или не вставляется совсем. - Для «добавить или обновить» —
ON DUPLICATE KEY UPDATEс псевдонимом строки и полными именами столбцов;INSERT IGNOREглушит лишние ошибки. NOT NULLбезDEFAULTприADD COLUMNмолча заполнит старые строки пустыми значениями, аMODIFYбез полного определения молча снимет ограничения.ALTER TABLEне откатывается транзакцией: копия, проверка на стенде и явныйALGORITHM.
Где применяется / связь с практикой
Добавление строк и столбцов — повседневная работа бэкенд-разработчика и аналитика: загрузка справочников, миграции схемы, импорт выгрузок. Изменять уже добавленные данные помогает UPDATE в SQL, а убирать лишние строки — удаление данных в SQL.
Освойте тему на практике
Системно разобрать проектирование таблиц, ограничения, транзакции и миграции можно на курсе «SQL для разработчиков и аналитиков». Попробовать формат обучения бесплатно — на открытых уроках Otus.
FAQ
Как быстро загрузить в MySQL тысячи строк из CSV?
Командой LOAD DATA INFILE: нужна привилегия FILE, а если secure_file_priv задан каталогом (в официальном Docker-образе это /var/lib/mysql-files/), сервер читает файлы только оттуда. Вариант LOAD DATA LOCAL с файлом клиента в MySQL 8.4 по умолчанию выключен (local_infile = 0) и включается осознанно на сервере и в клиенте.
Можно ли отменить ошибочный INSERT?
Да, если он выполнен внутри START TRANSACTION и еще не было COMMIT — тогда поможет ROLLBACK. В режиме autocommit строку придется удалять отдельным DELETE по ключу.
Чем DEFAULT отличается от NULL?
NULL — это отсутствие значения, а DEFAULT — значение, которое MySQL подставит, если столбец не указан в INSERT. Явно переданный NULL не заменяется значением по умолчанию: в столбец NOT NULL он вызовет ошибку 1048.



