Усі три способи записують дані, але працюють на різних рівнях.
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.