Menu

SQLite INSERT: вставка строк, массовая вставка и OR IGNORE

Разбираемся, как работает INSERT в SQLite: вставка одной строки, нескольких строк, INSERT...SELECT, значения по умолчанию и модификаторы OR IGNORE и OR REPLACE.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

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) — если нужен более тонкий контроль.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ