INSERT — добавление строк в таблицу
Команда INSERT нужна для того, чтобы записать новые строки в таблицу. Синтаксис компактный и легко запоминается:
На что стоит обратить внимание — три части:
INSERT INTO books— таблица, в которую вставляем данные.(title, author, year)— список колонок, для которых вы передаёте значения.VALUES (...)— сами значения, в том же порядке, что и колонки.
Колонки id в списке нет, поэтому SQLite присвоит её автоматически (это INTEGER PRIMARY KEY, который получает rowid). Любая пропущенная колонка получит значение по умолчанию, а если такого нет — NULL.
Всегда указывайте колонки явно
В принципе, список колонок можно опустить и передать значения сразу для всех колонок в порядке их объявления:
-- Работает, но ненадёжно:
INSERT INTO books VALUES (NULL, 'Dune', 'Frank Herbert', 1965);
Не делайте так. Стоит кому-то добавить новый столбец в books — и все подобные запросы либо сломаются, либо начнут писать значения не в те столбцы. Перечисляйте столбцы явно:
Когда вы явно перечисляете столбцы, это работает как живая документация — оператор становится понятным сам по себе, без необходимости лезть в определение таблицы.
Массовая вставка строк в SQLite
Чтобы вставить несколько строк за один запрос, достаточно перечислить кортежи значений через запятую:
Так получается чище, чем три отдельных INSERT, и SQLite обрабатывает всё это как один оператор. Но настоящий прирост скорости при массовой вставке даёт оборачивание INSERT в транзакцию — об этом дальше.
Массовая вставка в SQLite: оборачиваем в транзакцию
По умолчанию каждый INSERT — это отдельная транзакция. После каждой SQLite делает fsync, и именно из-за этого наивные циклы работают медленно — дело не в самих вставках.
Сгруппируем их:
Один fsync вместо пяти. На тысячах строк разница достигает двух-трёх порядков. Если что-то внутри транзакции упадёт, ROLLBACK откатит весь пакет целиком.
Это и есть базовый рецепт массовой вставки в SQLite. Неважно, откуда вы дёргаете базу — из Python, Node или Rust — оборачивайте свой цикл в BEGIN / COMMIT.
INSERT ... SELECT: копируем данные из другой таблицы
Заполнить таблицу можно не только литералами, но и результатом запроса:
Столбцы из SELECT сопоставляются с колонками INSERT по позиции, а не по имени. Порядок — вот что важно, имена могут отличаться. Это стандартный приём, когда нужно заархивировать строки, собрать отчётную таблицу или перенести часть данных при миграции.
DEFAULT VALUES и пропущенные столбцы
Если у столбца задано значение по умолчанию через DEFAULT, его можно вообще не указывать в списке колонок — SQLite сам подставит значение по умолчанию:
created_at получает текущую метку времени, потому что мы её не указали. Если же нужно вставить строку целиком из значений по умолчанию — например, как заготовку-плейсхолдер — используйте форму DEFAULT VALUES:
Две новые строки, обе с value = 0 и автоматически присвоенными id.
INSERT OR IGNORE: пропускаем дубликаты
Если новая строка нарушает ограничение UNIQUE или PRIMARY KEY, по умолчанию SQLite прерывает выполнение запроса с ошибкой:
Error: UNIQUE constraint failed: users.email
INSERT OR IGNORE меняет это поведение на «молча пропустить проблемную строку»:
Останется три строки. Дубликат отбрасывается без ошибки. Это идиоматичный для SQLite способ выразить «вставить, если ещё нет» для простых начальных данных — без отдельного SELECT для проверки и без обработки исключений.
INSERT OR REPLACE: перезапись дубликатов
INSERT OR REPLACE удаляет конфликтующую строку и вставляет на её место новую:
Один важный момент: REPLACE — это DELETE + INSERT, а не UPDATE. Если на удаляемую строку ссылались внешние ключи с ON DELETE CASCADE, дочерние записи тоже улетят. А все колонки, которые вы не перечислили в новом INSERT, сбросятся к значениям по умолчанию — старое значение не сохранится.
В большинстве сценариев «обновить, если есть, вставить, если нет» вам на самом деле нужен полноценный upsert через ON CONFLICT ... DO UPDATE. Об этом — на отдельной странице (sqlite upsert on conflict).
Краткий итог
INSERT INTO table (cols) VALUES (...)— базовая форма sqlite insert. Всегда явно перечисляйте колонки.- Для вставки нескольких строк в sqlite перечисляйте кортежи через запятую после
VALUES. - Для массовой вставки в sqlite оборачивайте
INSERTвBEGIN/COMMIT. INSERT INTO ... SELECT ...копирует строки из результата запроса.DEFAULT VALUESсоздаёт строку целиком из значений по умолчанию; пропущенные колонки тоже подставляются из дефолтов.INSERT OR IGNOREпропускает конфликтующие строки, аINSERT OR REPLACEзатирает их (через удаление и вставку).
Дальше: UPDATE
Вставка строк — это только половина дела. Вторая половина — менять уже существующие записи: увеличить счётчик, исправить опечатку, отметить заказ как отправленный. Этим занимается UPDATE, и у него есть свои нюансы, которые стоит освоить (особенно касается WHERE). Об этом — в следующей части.
Часто задаваемые вопросы
Как вставить строку в SQLite?
Базовый синтаксис такой: INSERT INTO table (col1, col2) VALUES (val1, val2);. Перечислять колонки необязательно, но крайне желательно — если в таблицу позже добавится новый столбец, запрос продолжит работать. Без списка колонок придётся передавать значения для всех столбцов и строго в том порядке, в котором они объявлены.
Как вставить сразу несколько строк в SQLite?
Просто перечислите кортежи через запятую после VALUES: INSERT INTO t (a, b) VALUES (1, 2), (3, 4), (5, 6);. Но если речь о настоящей массовой загрузке (тысячи строк), главный прирост скорости даёт не сам синтаксис с несколькими кортежами, а оборачивание всех вставок в одну транзакцию через BEGIN и COMMIT.
Что делает INSERT OR IGNORE в SQLite?
INSERT OR IGNORE молча пропускает строки, которые нарушают ограничение UNIQUE, PRIMARY KEY или NOT NULL, вместо того чтобы кидать ошибку. Конфликтная строка просто отбрасывается, а остальные продолжают вставляться. Удобно, когда нужно поведение «вставить, если ещё нет», и не хочется городить отдельную проверку существования.
Почему при INSERT возникает ошибка UNIQUE constraint failed?
SQLite нашёл строку с таким же значением в столбце с UNIQUE или PRIMARY KEY. Либо это реально дубль, либо вы повторно запускаете seed-скрипт. Вариантов три: INSERT OR IGNORE, чтобы пропустить дубликаты, INSERT OR REPLACE, чтобы перезаписать их, или ON CONFLICT ... DO UPDATE (upsert) — если нужен более тонкий контроль.