#Что означает ошибка
sqlite3.IntegrityError: UNIQUE constraint failed: users.email
Ошибка «UNIQUE constraint failed: table.column» возникает при попытке вставить или изменить строку так, что значение в уникальном столбце (или составном ключе) дублируется. Это защита от дублей, а не сбой базы.
Нужно решить, что делать с дублем: пропустить, обновить или исправить логику приложения.
SQLite поддерживает явную обработку конфликтов.
Повторное уникальное значение — не ошибка повреждения. Сначала определите, почему возник дубль; INSERT OR IGNORE может скрыть другие нарушения ограничений, а INSERT OR REPLACE может удалить существующую запись и породить каскадные изменения. Предпочитайте управляемую обработку конфликта и диагностику причины.
#Причины и решения
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Дубликат значения | Повторяется уникальный email/ключ | Найдите запись и определите бизнес-правило обработки дубля. |
| Повторная вставка | Запись повторно отправлена с формы/сети | Исправьте защиту от повторных запросов и обработку конфликта. |
| Неверный ключ | ON CONFLICT отсутствует или настроен на другой индекс | Используйте корректную цель конфликта и подходящую версию SQLite. |
| Регистр значений | При создании уникального индекса уже есть дубли | Сначала диагностируйте дубли на копии, удалять только осмысленно. |
#Пошаговое решение
- Прочитайте таблицу и столбец в ошибкеВ тексте ошибки найдите названия таблицы и столбцов после
UNIQUE constraint failed: users.emailили имя уникального индекса. Откройте SQL-редактор SQLite (например, консольsqlite3для копии базы), затем выполнитеSELECT sql FROM sqlite_schema WHERE type IN ('table','index') AND tbl_name='users';, заменивusersреальным именем. Это покажет, как устроены уникальные ограничения. Сообщение означает конфликт ключа, но не обязательно «испорченную» базу.Linux · Терминал (Bash)КомандаСхема таблицы и поиск существующей записи.sqlite3 db.sqlite ".schema users"sqlite3 db.sqlite "SELECT * FROM users WHERE email='a@example.com';" - Найдите конфликтующую записьЕсли конфликт возникает на
email, сначала в копии базы выполните запрос только на чтениеSELECT id, email FROM users WHERE email = 'user@example.com';, заменив значения реальными; для составного UNIQUE укажите все входящие столбцы. Не публикуйте личные данные в журналах. Если строка существует, определите, почему код пытается вставить её повторно: повторная отправка формы, импорт, конкурирующие запросы или ошибочный идентификатор. Если запись не находится, проверьте транзакции и правильность используемой базы. - Решите стратегиюРешение зависит от смысла данных. Если повторный запрос можно пропустить, приложение должно сообщать «уже существует» или корректно обрабатывать конфликт. Если запись нужно обновить, заранее определите, какие поля разрешено менять; не заменяйте все значения без проверки. Если ввод пользователя неверен, предложите исправить значение. Сначала испытайте вариант на тестовом экземпляре и сравните результат, не удаляйте существующую запись как универсальный выход.
- Используйте ON CONFLICTДля SQLite версии 3.24.0 и новее при подходящем уникальном ограничении можно использовать
INSERT INTO users (email, name) VALUES (?, ?) ON CONFLICT(email) DO NOTHING;либо согласованноеDO UPDATE SET name=excluded.name; знак?означает параметры, которые передаёт код приложения, а не значение для прямого ввода в терминале. Не путайте это со старымINSERT OR REPLACE: REPLACE может удалить существующую строку и создать новую, нарушая связи. ПроверьтеSELECT sqlite_version();и тесты на повторы. - Почистите дубли перед созданием индексаЕсли UNIQUE-индекс требуется добавить в существующую таблицу, сначала сделайте
.backup, затем выполните диагностическийSELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*)>1;на копии; замените поля реальными. Решение о слиянии/удалении дублей принимайте по бизнес-правилам: автоматическое удаление может разрушить ссылки на записи. После согласованной очистки создайте индекс через миграцию и проверьте целостность внешних ключей. Если ошибка появилась при вставке, создавать новый индекс обычно не нужно.
#Диагностика
В сообщении указаны таблица и столбец. Найдите конфликтующую строку: SELECT * FROM users WHERE email = 'a@b.ru';. Найдите существующие дубликаты: SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1;. Просмотрите схему: .schema users — какие UNIQUE и PRIMARY KEY есть. Помните, что NULL значения в уникальном индексе не конфликтуют.
#Обработка конфликтов
SQLite позволяет использовать INSERT ... ON CONFLICT(...) DO NOTHING/DO UPDATE для конкретного уникального индекса начиная с 3.24.0; цель конфликта должна соответствовать схеме. INSERT OR REPLACE может фактически удалить старую строку и нарушить ссылки — не используйте его как общий способ исправления.
#Исправление данных
Перед добавлением UNIQUE-индекса найдите дубли через GROUP BY ... HAVING COUNT(*)>1 на копии базы, затем определите по бизнес-правилам, как их объединять. Запросы автоматического удаления по rowid могут уничтожить связанные данные и недопустимы без согласованного плана. Если ошибка возникла при обычной вставке, новый индекс не нужен.
SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1;INSERT INTO users(email, name) VALUES('a@b.ru', 'Ann') ON CONFLICT(email) DO UPDATE SET name = excluded.name;