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

Чим upsert відрізняється від updateOrCreate і insert і що відбувається з подіями моделі?

Усі три способи записують дані, але працюють на різних рівнях.

updateOrCreate - модель за моделлю:

Price::updateOrCreate(
    ['product_id' => 42, 'currency' => 'UAH'],
    ['amount' => 1999],
);

Два запити (пошук і INSERT чи UPDATE), повний життєвий цикл моделі: події saving, created/updated, спостерігачі, мутатори й касти. Для тисячі рядків - тисячі пар запитів.

upsert - один запит на всю партію:

Price::upsert(
    [
        ['product_id' => 42, 'currency' => 'UAH', 'amount' => 1999],
        ['product_id' => 43, 'currency' => 'UAH', 'amount' => 2499],
    ],
    uniqueBy: ['product_id', 'currency'],
    update: ['amount'],
);

Перетворюється на INSERT ... ON CONFLICT (...) DO UPDATE у PostgreSQL чи INSERT ... ON DUPLICATE KEY UPDATE у MySQL. Eloquent додає created_at і updated_at.

insert - масове вставлення без перевірки існування; при дублікаті - помилка унікальності. insertOrIgnore - пропускає конфлікти мовчки.

Порівняння:

updateOrCreate upsert insert
запитів на N рядків ~2N 1 1
події моделі й спостерігачі так ні ні
касти й мутатори так ні (сирі значення) ні
timestamps так так ні
атомарність між процесами ні (гонка між SELECT і INSERT) так так

Що важливо знати про upsert:

  • колонки uniqueBy мають бути покриті первинним ключем чи унікальним індексом - на ньому база визначає конфлікт. Без індексу в PostgreSQL запит падає;
  • MySQL ігнорує uniqueBy і використовує будь-який унікальний індекс таблиці - результат може здивувати, якщо їх кілька;
  • події не спрацьовують: спостерігачі, що оновлюють кеш чи пошуковий індекс, не дізнаються про зміни - це треба зробити окремо;
  • касти не застосовуються: JSON-колонку треба передати вже закодованим рядком, enum - значенням.

Гонка в updateOrCreate: два паралельні запити можуть обидва не знайти запис і обидва спробувати вставити - один отримає помилку унікальності. upsert такої проблеми не має, бо конфлікт розв'язує сама база.

Коли що: одиничне збереження з бізнес-логікою в подіях - updateOrCreate; імпорт, синхронізація цін, лічильники - upsert.

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

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

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