Есть ли способ массового обновления (вставки или обновления, если существует) записей в Laravel (база данных MySQL)?
Скажем, у меня есть таблица с:
[id:1 value:4]
[id:2 value:5]
Я хочу выполнить:
Model::massupsert([
[id:1 value:100],
[id:3 value:20]
]);
И таблица после этого будет:
[id:1 value:100]
[id:2 value:5]
[id:3 value:20]
Я использую базу данных mysql.
В Laravel нет встроенной поддержки upsert (пока). Вы можете использовать этот пакет: github.com/yadakhov/insert-on-duplicate-key
Спасибо, это то, что я искал.
Если вы поставите это как ответ, я отмечу это как решение.






Конечно, вы можете использовать метод updateOrCreate(). Цитата из документация:
You may also come across situations where you want to update an existing model or create a new model if none exists. Laravel provides an updateOrCreate method to do this in one step.
$data = collect([
['id' => 1, 'value' => 100],
['id' => 3, 'value' => 20]
]);
$data->each(function ($item) {
Model::updateOrCreate([
['id' => $item['id']],
['value' => $item['value']]
]);
});
Примечание:, если вам нужно обрабатывать много записей одновременно, этот метод может привести к медленной обработке, так как вы создаете/обновляете одну модель в базе данных за раз.
Затем вы можете немного уменьшить количество запросов с помощью firstOrNew, так как вы просто массово вставляете новые записи в базу данных, но в настоящее время вам нужно вручную обновлять уже существующие записи по одной за раз:
$data = collect([
['id' => 1, 'value' => 100],
['id' => 3, 'value' => 20]
]);
$models = $data->map(function ($attributes) {
$model = Model::firstOrCreate([
['id' => $attributes['id']],
['value' => $attributes['value']]
]);
if (! $model->exists) {
$model->fill($attributes);
}
return $model;
});
list($saved, $unsaved) = $models->partition(function ($model) {
return $model->exists;
});
Model::insert($unsaved);
$saved->each->update();
В настоящее время, чтобы сделать это с помощью одного запроса, вы должны использовать пакет, предложенный Йонас Стауденмейр в комментариях.
Спасибо, но я ищу решение, которое приводит к 1 SQL-запросу, а не к нескольким вызовам (для повышения производительности).
@Yaron Нашли ли вы лучшее решение для уменьшения размера запроса?
В Laravel нет встроенной поддержки UPSERT.
Я создал для этого пакет: https://github.com/staudenmeir/laravel-upsert
Model::upsert([
['id' => 1, 'value' => 100],
['id' => 3, 'value' => 20],
], 'id', ['value']);
используйте это, и это сработало отлично, только одно, хотя из инструкций вы должны добавить use \Staudenmeir\LaravelUpsert\Eloquent\HasUpsertQueries; к классу модели;
Теперь (6 октября 2020 г.) Laravel (v8.10.0) имеет встроенную поддержку upsert. https://github.com/laravel/framework/pull/34698
Model::upsert([
['id' => 1, 'value' => 100],
['id' => 3, 'value' => 20],
], 'id');
Круто, но похоже, что Laravel просто создает несколько операторов INSERT ... ON DUPLICATE KEY UPDATE, что действительно очень медленно, когда ваш массив больше. Если производительность важна, то использование необработанного оператора один может быть намного быстрее.
В Laravel 8 теперь есть функция upsert, которую я использую в своем последнем проекте. Я очень наслаждаюсь этим!
https://laravel.com/docs/8.x/queries#update-statements
Метод upsert будет вставлять несуществующие записи и обновлять уже существующие записи новыми значениями, которые вы можете указать. Первый аргумент метода состоит из значений для вставки или обновления, а второй аргумент перечисляет столбцы, которые однозначно идентифицируют записи в связанной таблице. Третий и последний аргумент метода — это массив столбцов, который следует обновить, если соответствующая запись уже существует в базе данных:
DB::table('flights')->upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);
Какую базу данных вы используете?