TGViewer
Zen of Python Zen of Python @zen_of_python · 18.8K subscribers
Post #4305 2.79K
Молчаливый «провал» INSERT

Вы запускаете SQL-запрос INSERT, и вроде всё просто. Нет ошибок. Но и данные не вставлены. Звучит странно? Такое действительно может случиться с PostgreSQL — и случается чаще, чем хотелось бы.


Как можно вставить данные и не вставить одновременно?

Когда INSERT не срабатывает, первое, что приходит в голову — ошибка. Но PostgreSQL умеет «глотать» такое — ведь вы сами его об этом попросили.

Виновник — ON CONFLICT DO NOTHING



INSERT INTO users (id, email)
VALUES (42, 'user@example.com')
ON CONFLICT (id) DO NOTHING;


Здесь мы явно говорим: "если произойдёт конфликт по id, ничего не делай". И PostgreSQL по умолчанию так и поступает.


Поведение UPSERT при множественных уникальных индекса

Представьте, что в таблице есть не один, а два уникальных индекса:


CREATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT UNIQUE,
username TEXT UNIQUE
);


Теперь вы выполняете:


INSERT INTO users (email, username)
VALUES ('user@example.com', 'johnny');


А если username = 'johnny' уже существует, но email ещё нет?

Вставка завершится ошибкой!

Так происходит, потому что ON CONFLICT (email) говорит PostgreSQL: «молчи, если конфликт по email, но бросай ошибку, если конфликт по чему-то ещё».

Чтобы избежать этого, используем:


ON CONFLICT DO NOTHING


Тогда PostgreSQL проигнорирует конфликт по любому индексу. Но это может спровоцировать проблемы, особенно при вставке пачкой


Как отладить такую ситуацию?

— Проверяйте rowcount после запроса. В Python/psycopg2, например:


cursor.execute(sql, values)
if cursor.rowcount == 0:
print("Nothing inserted!")


— Добавьте RETURNING и логируйте:


INSERT INTO users (email, username)
VALUES ('user@example.com', 'johnny')
ON CONFLICT DO NOTHING
RETURNING id;


Если возвращается пустой результат — значит, вставки не было.

— Логируйте причину. Если вы используете логику вида DO UPDATE, можно добавить логи в `UPDATE`-часть или сохранять «причину отказа» отдельно.

#sql
  • 👍 9
  • ❤ 1
  • 🗿 1
More from @zen_of_python
  1. Sep 28, 2026partial или lambda: как специализировать колбэк без сюрпризов functools.partial заранее св…
  2. Sep 28, 2026Как коллизии превращают построение set в Python в квадратичную задачу Привычное O(1) для s…
  3. Sep 28, 2026Как проверять инварианты Python-кода с Hypothesis Обычный тест фиксирует конкретный ввод и…
  4. Sep 27, 2026Как запускать собственный SQL через миграции Django У models.Index нашлось необычное приме…
  5. Sep 27, 2026Как разделить синхронный и асинхронный Python-клиенты При переписывании akismet автор отка…
  6. Sep 27, 2026Как заменить цепочку isinstance на singledispatch Когда обработка типов разрастается, цепо…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →