Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Чим MERGE відрізняється від INSERT ... ON CONFLICT і коли його обрати?

MERGE (PostgreSQL 15+, стандартний SQL) порівнює цільову таблицю з джерелом і для кожного рядка виконує дію залежно від того, знайдено збіг чи ні:

MERGE INTO products AS p
USING staging_products AS s
ON p.sku = s.sku
WHEN MATCHED AND s.discontinued THEN
    DELETE
WHEN MATCHED THEN
    UPDATE SET price = s.price, updated_at = now()
WHEN NOT MATCHED THEN
    INSERT (sku, name, price) VALUES (s.sku, s.name, s.price);

PostgreSQL 17+: WHEN NOT MATCHED BY SOURCE - рядки цільової таблиці, яких немає в джерелі (наприклад, видалити товари, що зникли з фіда), і RETURNING з функцією merge_action(), що показує, яку дію виконано для рядка.

Порівняння:

INSERT ... ON CONFLICT MERGE
дії вставити чи оновити вставити, оновити, видалити, нічого
умова збігу лише унікальний індекс чи обмеження довільна умова ON
потрібен унікальний індекс так ні
атомарність при паралельних вставках так, конфлікт розв'язує індекс ні - можливі помилки унікальності
джерело VALUES чи SELECT таблиця, підзапит, VALUES

Головна відмінність - поведінка при конкуренції. ON CONFLICT гарантовано обробляє ситуацію, коли інший запит одночасно вставляє той самий ключ: він чекає й оновлює. MERGE спершу визначає, чи є збіг, а потім виконує дію - якщо паралельний запит встиг вставити рядок, MERGE спробує вставити його вдруге й отримає помилку унікальності.

Коли що обирати:

  • ON CONFLICT - upsert з високою конкуренцією: лічильники, кеш-таблиці, синхронізація окремих записів з багатьох процесів;
  • MERGE - пакетна синхронізація з таблицею-джерелом: імпорт фіда, ETL з проміжної таблиці, де потрібні і вставки, і оновлення, і видалення, а паралельних записувачів немає (чи їх виключено блокуванням).

Типовий сценарій імпорту:

  1. COPY файлу в тимчасову таблицю;
  2. MERGE з неї в робочу таблицю;
  3. одна транзакція - або все застосовано, або нічого.

У Laravel конструктор запитів не має методу для MERGE - використовують DB::statement() з параметрами.

Докладніше в документації: MERGE

Схожі питання