Dirilis pada 13 Oktober 2020, Laravel 8.10 hadir dengan penambahan upsert() pada pembuat kueri database (dan karena itu kueri berbasis Eloquent juga). Ini memungkinkan Anda melakukan kueri "sisipkan atau perbarui" jauh lebih efisien yang akan membantu saat mengimpor data dalam jumlah besar.
Q : Mengapa menggunakannya? , Bagaimana cara kerjanya? , Bagaimana kinerjanya? Bagaimana cara saya
menggunakannya?
Contoh praktis penggunaan upsert ()
Hingga saat ini, pembuat kueri basis data Laravel mendukung penyisipan atau pembaruan beberapa baris (kumpulan data) sekaligus melalui metode insert() dan update(). Ada juga insertOrIgnore() yang kurang dikenal yang akan memasukkan sekumpulan data, mengabaikan kesalahan apa pun (seperti kunci unik yang sudah ada).
Laravel juga memiliki updateOrCreate() pada model Eloquent yang memberikan sekumpulan atribut, memperbarui model yang ada, atau jika tidak ada, model yang dibuat. Namun, ini hanya dapat digunakan pada satu baris / model dalam satu waktu - jika kita memiliki 125.000 baris untuk disisipkan atau diperbarui, kita harus memanggil metode Eloquent itu sebanyak 125.00 kali. Di balik layar, ini sebenarnya mengeksekusi dua kueri dalam database satu untuk memeriksa entri yang ada dan satu lagi untuk melakukan INSERT atau UPDATE sehingga berarti menjalankan 250.000 kueri. Itu bisa lambat.
Jadi mari kita bayangkan kita memiliki file CSV yang berisi data untuk 125.000 produk yang perlu kita impor ke database kita seefisien mungkin. Tidak masalah, kita bisa menggunakan insert() pada pembuat kueri untuk menyisipkan beberapa baris sekaligus.
Kita percepat ke 4 minggu kemudian, kami memiliki file CSV data produk baru dengan rincian 125.000 produk telah diperbarui dan 25.000 produk baru telah ditambahkan. Dalam database kami, kami perlu memperbarui produk yang ada dan memasukkan yang baru. Untuk melakukan ini, kita perlu mengetahui produk mana yang sudah kita miliki, dan mana yang baru sehingga kita dapat melakukan kueri INSERT atau kueri UPDATE.
Daripada menggunakan updateOrCreate() 150.000 kali untuk setiap baris dalam file CSV, mari kita mulai dengan memecah file CSV menjadi beberapa kelompok yang lebih kecil, misalnya 5.000 produk. Itu memberi kami 30 batch.
Kemudian kita dapat meneruskan setiap kumpulan data produk ke upsert() di kelas Illuminate\Database\Query\Builder, yang untuk setiap baris data dalam file CSV akan memperbarui produk yang ada di database kita, atau membuat yang baru. Dengan cara ini kami hanya akan mengeksekusi 30 kueri daripada 300.000 jika kami menggunakan updateOrCreate() (ingat, sebenarnya ini ada 2 kueri untuk setiap penggunaannya).
Jadi bagaimana cara kerja upsert ()?
Saya akan menganggap Anda menggunakan MySQL, meskipun itu berfungsi dengan mesin database lain.
Mengikuti dari contoh database produk kami, yang disederhanakan untuk kumpulan hanya 3 produk, berikut adalah SQL yang akan dijalankan:
INSERT INTO products
(item_ref, title, price)
VALUES
(1, 'Large cake', 15.00),
(1, 'Medium cake', 10.00),
(1, 'Small cake', 5.00)
ON DUPLICATE KEY
UPDATE
item_ref = values(item_ref),
title = values(title),
price = values(price);
Seperti yang Anda lihat, kueri dimulai dengan pernyataan normal INSERT dan akan mencoba memasukkan kumpulan nilai data pertama ke dalam tabel. Jika berhasil, ini akan dipindahkan ke set berikutnya. Namun, jika gagal karena pengenalan unik (dalam contoh kita, yaitu "item_ref") sudah ada (yaitu baris data sudah ada di tabel), maka itu akan ditangkap oleh ON DUPLCIATE KEY bagian dari kueri. SQL setelah itu, dalam hal ini sebuah pernyataan UPDATE, kemudian akan dijalankan. Ini akan memperbarui baris database yang ada dengan sekumpulan nilai. Setelah selesai, ini akan pindah ke kumpulan nilai data berikutnya.
Bagaimana kinerja upsert ()?
Untuk menguji kinerja upsert(), saya memulai proyek Laravel baru dan membuat perintah artisan sederhana yang melakukan 3 pengujian menggunakan updateOrCreate , menggunakan upsert() metode untuk baris tunggal, dan menggunakan upsert() dengan kumpulan baris (5.000 pada suatu waktu).
Untuk setiap pengujian, larik 125.000
"produk" ditulis ke tabel kosong - mengukur waktu yang dibutuhkan
untuk menyelesaikannya. Setiap item dalam larik asli data produk kemudian
diperbarui dengan harga baru, dan 25.000 produk ditambahkan. Data mentah
yang diperbarui ini kemudian juga ditulis ke database sementara waktunya.
Saya memutar server baru di Linode (dengan PHP 7.4 & MySQL 8) dan menjalankan perintah artisan 5 kali. Saya menghitung rata-rata waktu dari setiap tes, yang berakhir sebagai berikut:
Tes 1 ( updateOrCreate()metode)
·
INSERT: 280,84 detik
·
INSERT / UPDATE: 398,76 detik
Tes 2 ( upsert()metode - baris tunggal)
·
INSERT : 203,99 detik
·
INSERT / UPDATE: 270.39 detik
Tes 3 ( upsert()metode - baris bertumpuk)
·
INSERT: 15,38 detik
· INSERT / UPDATE: 17.97 detik
Seperti yang diharapkan, tes pertama dengan updateOrCreate() yang paling lambat. Pengujian kedua, menggunakan upsert() untuk setiap produk satu per satu sedikit lebih cepat karena hanya mengeksekusi 1 kueri untuk setiap produk, bukan 2. Terakhir, pengujian ketiga, menggunakan upsert() untuk batch (5.000) produk jauh lebih cepat dibandingkan dua tes lainnya. Dalam pengujian saya, ini bekerja pada 95% lebih cepat untuk kueri INSERT dan kueri INSERT / UPDATE , jika dibandingkan dengan updateOrCreate().
Bagaimana saya bisa menggunakan upsert ()?
Metode ini tersedia dari pembuat kueri
database Laravel, dan pembuat kueri Eloquent. Ini menerima 3 argumen:
upsert(array
$values, $uniqueBy, $update = null)
$ Values
Argumen pertama adalah array array yang
berisi data yang akan disisipkan / diperbarui - satu array anak untuk setiap
baris model / database Eloquent. Sebagai contoh:
$values = [
[
'item_ref' => 1,
'title' => 'Large cake',
'price' => 15.00,
],
[
'item_ref' => 2,
'title' => 'Medium cake',
'price' => 10.00,
],
[
'item_ref' => 3,
'title' => 'Small cake',
'price' => 5.00,
]
];
Jika Anda menggunakan upsert() model Eloquent yang menggunakan
stempel waktu, atribut created_at dan updated_at akan ditangani secara otomatis.
$ uniqueBy
Argumen kedua menjelaskan cara
mengidentifikasi baris unik.
Ini bisa berupa nama kolom tunggal (diteruskan sebagai string, atau larik yang berisi string tunggal), atau jika Anda memiliki kunci komposit (dua kolom atau lebih yang digunakan untuk mengidentifikasi baris secara unik), nama beberapa kolom diteruskan sebagai sebuah array. Sebagai contoh:
$uniqueBy = 'item_ref';
// OR
$uniqueBy = ['item_ref'];
// OR
$uniqueBy = ['item_type', 'item_size'];
Semua mesin database kecuali
SQL Server memerlukan kolom dalam argumen $ uniqueBy untuk memiliki indeks
"primer" atau "unik".
$update
Argumen ketiga dan terakhir, yang bersifat opsional, menentukan kolom yang harus diperbarui jika baris yang cocok sudah ada dalam database. Ini memungkinkan Anda untuk memasukkan data dalam $values argumen Anda yang hanya akan ditulis di INSERT. Secara default, semua kolom di Anda $values diperbarui, tetapi berikut adalah contoh jika Anda ingin memberikan argumen ini:
$update = ['title', 'price'];