Добавление информации в MySQL: INSERT строк и новые столбцы с параметрами

Добавление информации в MySQL: INSERT строк и новые столбцы с параметрами Полезное

Добавить информацию в таблицу MySQL можно двумя разными командами. INSERT добавляет новые строки (записи) в существующие столбцы. ALTER TABLE … ADD COLUMN добавляет новый столбец, а его параметры — тип, NULL/NOT NULL, DEFAULT, CHECK — задаются прямо в определении столбца. Изменить параметр уже существующего столбца можно через ALTER TABLE ... ALTER COLUMN (значение по умолчанию и видимость столбца) или MODIFY COLUMN (полное определение). UPDATE ничего не добавляет — он меняет строки, которые уже есть.

Ниже — учебный стенд, вставка одной и нескольких строк, 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 так не получится: команда неявно завершает текущую транзакцию. Поэтому порядок такой:

  1. Сделать резервную копию таблицы и проверить, что она восстанавливается.
  2. Прогнать ту же команду на копии с похожим объемом данных.
  3. Указать ALGORITHM = INSTANT или INPLACE явно: если операция требует копирования таблицы, MySQL откажет сразу, а не начнет долгую перестройку.
  4. Проверить долгие транзакции: ALTER ждет блокировку метаданных, а пока ждет, за ним встают обычные запросы к таблице.
  5. Заранее написать откат (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.

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)