TGViewer
Библиотека шарписта | C#, F#, .NET, ASP.NET Библиотека шарписта | C#, F#, .NET, ASP.NET @csharpproglib · 21.7K subscribers
Post #7039 2.73K
👩‍💻 Когда ограничения мешают вставке

Иногда при работе с PostgreSQL нужно вставить несколько связанных записей в одной транзакции, но внешние ключи проверяются сразу и транзакция падает. DEFERRABLE INITIALLY DEFERRED решает эту проблему — ограничение откладывается до конца транзакции.

Что это и зачем

По умолчанию PostgreSQL проверяет ограничения (FOREIGN KEY, UNIQUE, CHECK) сразу при выполнении каждого оператора. Если порядок вставки нарушает ссылочную целостность — получаем ошибку, даже если к концу транзакции всё было бы корректно.

DEFERRABLE INITIALLY DEFERRED говорит базе: не проверяй ограничение после каждой операции — подожди до COMMIT.

Это удобно, когда:
- записи ссылаются друг на друга (циклические FK)
- порядок вставки нельзя гарантировать
- нужно пакетно импортировать данные без временного отключения ограничений

Пример схемы

Допустим, есть две таблицы, которые ссылаются друг на друга:
CREATE TABLE teams (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
lead_id INT REFERENCES employees(id) DEFERRABLE INITIALLY DEFERRED
);

CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
team_id INT REFERENCES teams(id) DEFERRABLE INITIALLY DEFERRED
);


Без DEFERRABLE INITIALLY DEFERRED вставить команду со ссылкой на сотрудника, которого ещё нет — невозможно. С ним — всё проверяется один раз при COMMIT.

Как использовать в C# с Npgsql

Npgsql поддерживает транзакции напрямую. Главное — убедиться, что обе вставки выполняются в одной транзакции.
await using var conn = new NpgsqlConnection(connectionString);
await conn.OpenAsync();

await using var tx = await conn.BeginTransactionAsync();

// Вставляем команду, но сотрудник ещё не существует
await using (var cmd = new NpgsqlCommand(
"INSERT INTO teams (id, name, lead_id) VALUES (1, 'Backend', 42)", conn, tx))
{
await cmd.ExecuteNonQueryAsync();
}

// Теперь вставляем сотрудника
await using (var cmd = new NpgsqlCommand(
"INSERT INTO employees (id, name, team_id) VALUES (42, 'Иван', 1)", conn, tx))
{
await cmd.ExecuteNonQueryAsync();
}

// FK проверяются здесь — оба существуют, всё ок
await tx.CommitAsync();


Без DEFERRABLE INITIALLY DEFERRED первая вставка упала бы с ошибкой foreign key violation, потому что employees(42) не существует в момент вставки команды.

Как использовать с EF Core

EF Core сам по себе не умеет объявлять DEFERRABLE ограничения через Fluent API. Но можно сделать через HasCheckConstraint с raw SQL или через миграцию.

Самый надёжный способ — написать миграцию вручную:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"
ALTER TABLE teams
DROP CONSTRAINT IF EXISTS teams_lead_id_fkey;

ALTER TABLE teams
ADD CONSTRAINT teams_lead_id_fkey
FOREIGN KEY (lead_id) REFERENCES employees(id)
DEFERRABLE INITIALLY DEFERRED;
");
}


После этого транзакции EF Core будут пользоваться отложенной проверкой автоматически — ничего дополнительно настраивать не нужно.

Разница между DEFERRED и IMMEDIATE

DEFERRABLE означает, что ограничение можно отложить. INITIALLY DEFERRED — что оно отложено по умолчанию для каждой транзакции.

Если нужно временно изменить поведение внутри конкретной транзакции — можно явно переключить режим:
-- Внутри транзакции включить немедленную проверку
SET CONSTRAINTS teams_lead_id_fkey IMMEDIATE;

-- Или отложить конкретное ограничение
SET CONSTRAINTS teams_lead_id_fkey DEFERRED;


В C# через Npgsql это выполняется как обычный SQL-запрос внутри транзакции:
await using var cmd = new NpgsqlCommand(
"SET CONSTRAINTS ALL DEFERRED", conn, tx);
await cmd.ExecuteNonQueryAsync();


📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
  • 👍 3
  • 🤔 1
More from @csharpproglib
  1. Sep 22, 2026💡 Replace, Regex или StringBuilder? Для замены текста в C# есть несколько инструментов. И…
  2. Sep 21, 2026⚙️ Настоящие атомарные операции Если Volatile решает проблему видимости, то Interlocked ре…
  3. Sep 20, 2026💪 Разминка перед трудовыми буднями Что произойдёт? ❤️ — список станет [1, 3] 🔥 — Invalid…
  4. Sep 19, 2026🧩 Middleware в ASP.NET Core: 3 ловушки Middleware — звено HTTP pipeline: app.Use(async (c…
  5. Sep 18, 2026📍 Навигация: Вакансии • Задачи • Собесы 🐸Библиотека шарписта #garbage_collector
  6. Sep 17, 2026🦆 Duck typing в C# — почти как в TypeScript В TypeScript достаточно, чтобы у объекта был…
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 →