Есть ли способ массового обновления (вставки или обновления, если существует) записей в 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']);
Какую базу данных вы используете?