One solution is to use a database transaction with a SELECT FOR UPDATE statement to lock the rows being checked and prevent other processes from inserting the same values at the same time. Here's an example using Laravel's query builder:
DB::transaction(function () use ($values) {
// Lock the rows being checked
DB::table('my_table')->whereIn('my_column', $values)->sharedLock()->get();
// Check if any of the values exist
$existingValues = DB::table('my_table')->whereIn('my_column', $values)->pluck('my_column')->toArray();
// Insert the values that don't exist
$newValues = array_diff($values, $existingValues);
if (!empty($newValues)) {
DB::table('my_table')->insert(array_map(function ($value) {
return ['my_column' => $value];
}, $newValues));
}
});
Explanation:
-
The
DB::transactionmethod wraps the database operations in a transaction, which ensures that either all of the operations succeed or none of them do. This is important to prevent race conditions where multiple processes try to insert the same values at the same time. -
The
whereInmethod is used to select the rows that match any of the values in the$valuesarray. ThesharedLockmethod is called to lock the selected rows for reading, which prevents other processes from inserting the same values until the transaction is committed or rolled back. -
The
pluckmethod is used to extract the values of themy_columncolumn from the selected rows. ThetoArraymethod is called to convert the collection to a plain array. -
The
array_difffunction is used to find the values in$valuesthat don't exist in$existingValues. -
The
array_mapfunction is used to transform the$newValuesarray into an array of associative arrays that can be inserted into the database using theinsertmethod.
Note that this solution assumes that the my_column column has a unique constraint or index to prevent duplicate values. If this is not the case, you may need to add a unique constraint or index to the column or use a different approach to prevent duplicates.