Saat ini saya menggunakan tipe SQL ini di MySQL untuk memasukkan beberapa baris nilai dalam satu permintaan tunggal:
INSERT INTO `tbl` (`key1`,`key2`) VALUES ('r1v1','r1v2'),('r2v1','r2v2'),...
Pada bacaan di PDO, pernyataan penggunaan yang disiapkan harus memberi saya keamanan yang lebih baik daripada pertanyaan statis.
Karena itu saya ingin tahu apakah mungkin untuk menghasilkan "memasukkan beberapa baris nilai dengan menggunakan satu permintaan" menggunakan pernyataan yang disiapkan.
Jika ya, boleh saya tahu bagaimana saya bisa menerapkannya?
php
pdo
insert
prepared-statement
hoball
sumber
sumber
$stmt->execute($data);
php.net/manual/en/... Pada dasarnya semua params disahkan divalidasi sebagai string. Hanya perulangan data setelah membangun kueri, dan secara manualbindValue
ataubindParam
mengirimkan tipe sebagai argumen ketiga.Jawaban:
Beberapa Nilai Masukkan dengan Laporan Disiapkan PDO
Memasukkan banyak nilai dalam satu pernyataan eksekusi. Mengapa karena menurut halaman ini lebih cepat dari sisipan biasa.
lebih banyak nilai data atau Anda mungkin memiliki loop yang mengisi data.
Dengan sisipan yang sudah disiapkan, Anda perlu mengetahui bidang yang Anda sisipkan, dan jumlah bidang yang akan dibuat? placeholder untuk mengikat parameter Anda.
Pada dasarnya itulah yang kita inginkan seperti pernyataan insert.
Sekarang, kodenya:
Meskipun dalam pengujian saya, hanya ada perbedaan 1 detik saat menggunakan beberapa sisipan dan sisipan reguler yang disiapkan dengan nilai tunggal.
sumber
placeholders()
berulang kali. Sebut satu kali sebelum loop dengansizeof($datafields)
dan tambahkan string hasil ke$question_marks[]
dalam loop.Jawaban yang sama seperti Tuan Balagtas, sedikit lebih jelas ...
Versi terbaru MySQL dan PHP PDO melakukan dukungan multi-baris
INSERT
pernyataan.Ikhtisar SQL
SQL akan terlihat seperti ini, dengan asumsi tabel 3-kolom yang Anda inginkan
INSERT
.ON DUPLICATE KEY UPDATE
berfungsi seperti yang diharapkan bahkan dengan INSERT multi-baris; tambahkan ini:Tinjauan PHP
Kode PHP Anda akan mengikuti panggilan biasa
$pdo->prepare($qry)
dan$stmt->execute($params)
PDO.$params
akan menjadi array 1 dimensi dari semua nilai untuk diteruskan keINSERT
.Pada contoh di atas, harus mengandung 9 elemen; PDO akan menggunakan setiap set 3 sebagai satu baris nilai. (Memasukkan 3 baris 3 kolom masing-masing = 9 elemen array.)
Penerapan
Kode di bawah ini ditulis untuk kejelasan, bukan efisiensi. Bekerja dengan
array_*()
fungsi PHP untuk cara yang lebih baik untuk memetakan atau menelusuri data Anda jika Anda mau. Apakah Anda dapat menggunakan transaksi jelas tergantung pada jenis tabel MySQL Anda.Asumsi:
$tblName
- nama string tabel untuk INSERT ke$colNames
- array 1-dimensi dari nama kolom dari tabel. Nama-nama kolom ini harus merupakan pengidentifikasi kolom MySQL yang valid; melarikan diri mereka dengan backticks (``) jika tidak$dataVals
- array mutli-dimensional, di mana setiap elemen adalah array 1-d dari deretan nilai untuk INSERTKode sampel
sumber
$rowPlaces
tidak perlu lagi:$allPlaces = implode(',', array_fill(0, count($dataVals), '('.str_pad('', (count($colNames)*2)-1, '?,').')'));
votes
ADD UNIKunique_index
(user
,email
,address
);array_push($dataToInsert, ...array_values($dataVals));
akan jauh lebih cepatforeach ($dataVals as $row => $data) {}
Untuk apa nilainya, saya telah melihat banyak pengguna merekomendasikan iterasi melalui pernyataan INSERT alih-alih membangun sebagai permintaan string tunggal seperti jawaban yang dipilih lakukan. Saya memutuskan untuk menjalankan tes sederhana hanya dengan dua bidang dan pernyataan sisipan yang sangat dasar:
Sementara kueri keseluruhan itu sendiri mengambil milidetik atau kurang, kueri yang terakhir (string tunggal) secara konsisten 8 kali lebih cepat atau lebih. Jika ini dibangun untuk mengatakan mencerminkan impor ribuan baris pada lebih banyak kolom, perbedaannya bisa sangat besar.
sumber
bind_param
pernyataan dalam rutin impor kedua"?(?,?)
, bukan?Jawaban yang Diterima oleh Herbert Balagtas bekerja dengan baik ketika array $ data kecil. Dengan array $ data yang lebih besar, fungsi array_merge menjadi sangat lambat. File pengujian saya untuk membuat array data $ memiliki 28 cols dan sekitar 80.000 baris. Script terakhir mengambil 41 untuk menyelesaikan.
Menggunakan array_push () untuk membuat $ insert_values daripada array_merge () menghasilkan kecepatan 100X dengan waktu eksekusi 0,41 detik .
Array_merge yang bermasalah ():
Untuk menghilangkan perlunya array_merge (), Anda dapat membangun dua array berikut:
Array ini kemudian dapat digunakan sebagai berikut:
sumber
array_push($data, ...array_values($row))
bukan$data = array_merge($data, array_values($row));
. Lebih cepat.array_push()
tersedia bahkan di php 4.array_push()
, tetapi karena @Mark menggunakan argumen unpacking. Perhatikan...array_values()
panggilan di sana?array_values()
juga tersedia di php 4. Tidak yakin apakah itu yang Anda maksudargument unpacking
.Dua kemungkinan pendekatan:
Atau:
Jika data untuk semua baris dalam satu array, saya akan menggunakan solusi kedua.
sumber
$stmt->execute();
harus berada di luar foreach loop?Itu sama sekali bukan cara Anda menggunakan pernyataan yang disiapkan.
Sangatlah oke untuk menyisipkan satu baris per kueri karena Anda dapat menjalankan satu pernyataan yang dipersiapkan beberapa kali dengan parameter yang berbeda. Sebenarnya itu adalah salah satu keuntungan terbesar karena memungkinkan Anda memasukkan banyak baris dengan efisien, aman, dan nyaman.
Jadi mungkin untuk mengimplementasikan skema yang Anda usulkan, setidaknya untuk jumlah baris tetap, tetapi hampir dijamin bahwa ini bukan yang Anda inginkan.
sumber
Jawaban yang lebih pendek: ratakan array data yang dipesan oleh kolom lalu
Saat menyisipkan 1.000 atau lebih catatan, Anda tidak ingin harus mengulang setiap catatan untuk memasukkannya ketika semua yang Anda butuhkan adalah hitungan nilai.
sumber
Inilah pendekatan sederhana saya.
sumber
On the readings on PDO, the use prepared statements should give me a better security than static queries.
$workouts_id
, yang dapat dilakukan$value
dengan data yang tidak terduga. Anda tidak dapat menjamin bahwa mungkin tidak sekarang tetapi di masa depan pengembang lain membuat data ini tidak aman. Jadi saya pikir lebih tepat membuat kueri yang disiapkan oleh PDO.Inilah kelas yang saya tulis lakukan beberapa sisipan dengan opsi pembersihan:
sumber
Beginilah cara saya melakukannya:
Pertama-tama tentukan nama kolom yang akan Anda gunakan, atau biarkan kosong dan pdo akan menganggap Anda ingin menggunakan semua kolom pada tabel - dalam hal ini Anda harus menginformasikan nilai-nilai baris dalam urutan yang tepat yang muncul di atas tabel .
Sekarang, misalkan Anda memiliki array dua dimensi yang sudah disiapkan. Iterasi, dan buat string dengan nilai baris Anda, seperti:
Sekarang, yang baru saja Anda lakukan adalah memeriksa apakah $ rows sudah didefinisikan, dan jika tidak, buat dan simpan nilai-nilai baris dan sintaks SQL yang diperlukan sehingga itu akan menjadi pernyataan yang valid. Perhatikan bahwa string harus masuk ke dalam tanda kutip ganda dan tanda kutip tunggal, sehingga mereka akan segera dikenali seperti itu.
Yang perlu dilakukan adalah menyiapkan pernyataan dan mengeksekusi, dengan demikian:
Diuji dengan 2000 baris sejauh ini, dan waktu pelaksanaannya suram. Akan menjalankan beberapa tes lagi dan akan kembali ke sini kalau-kalau ada sesuatu yang lebih lanjut untuk saya sumbangkan.
Salam.
sumber
Karena belum disarankan, saya cukup yakin LOAD DATA INFILE masih merupakan cara tercepat untuk memuat data karena menonaktifkan pengindeksan, memasukkan semua data, dan kemudian mengaktifkan kembali indeks - semua dalam satu permintaan.
Menyimpan data sebagai csv harus cukup sepele mengingat fputcsv. MyISAM adalah yang tercepat, tetapi Anda masih mendapatkan kinerja besar di InnoDB. Ada kelemahan lain, jadi saya akan memilih rute ini jika Anda memasukkan banyak data, dan tidak repot dengan di bawah 100 baris.
sumber
Meskipun pertanyaan lama, semua kontribusi sangat membantu saya, jadi inilah solusi saya, yang bekerja di dalam
DbContext
kelas saya sendiri . The$rows
parameter hanyalah sebuah array dari array asosiatif mewakili baris atau model:field name => insert value
.Jika Anda menggunakan pola yang menggunakan model ini cocok dengan baik ketika melewati data model sebagai array, katakanlah dari
ToRowArray
metode dalam kelas model.sumber
$tableName
terkena pengguna, yang bukan, itu di DAL. Bisakah Anda memperluas klaim Anda? Tidak membantu hanya mengatakan sesuatu.$tableName
?Berikut ini adalah solusi lain (ramping) untuk masalah ini:
Pada awalnya Anda perlu menghitung data dari array sumber (di sini: $ aData) dengan count (). Kemudian Anda menggunakan array_fill () dan menghasilkan array baru yang memiliki banyak entri seperti yang dimiliki array sumber, masing-masing dengan nilai "(?,?)" (Jumlah placeholder tergantung pada bidang yang Anda gunakan; di sini: 2). Maka array yang dihasilkan perlu di-implode dan sebagai lem digunakan koma. Dalam loop foreach, Anda perlu membuat indeks lain mengenai jumlah placeholder yang Anda gunakan (jumlah placeholder * indeks array saat ini + 1). Anda perlu menambahkan 1 ke indeks yang dihasilkan setelah setiap nilai yang diikat.
sumber
Anda bisa menyisipkan banyak baris dalam satu permintaan dengan fungsi ini:
$ row adalah array array nilai. Dalam kasus Anda, Anda akan memanggil fungsi dengan
Ini memiliki manfaat yang Anda gunakan pernyataan yang disiapkan , sambil menyisipkan beberapa baris dengan satu permintaan. Keamanan!
sumber
Ini solusi saya: https://github.com/sasha-ch/Aura.Sql berdasarkan pustaka auraphp / Aura.Sql.
Contoh penggunaan:
Laporan bug dipersilakan.
sumber
Contoh dunia nyata saya untuk memasukkan semua kode pos Jerman ke tabel kosong (untuk menambahkan nama kota nanti):
Seperti yang Anda lihat, ini sepenuhnya fleksibel. Anda tidak perlu memeriksa jumlah kolom atau memeriksa posisi kolom Anda. Anda hanya perlu mengatur data yang dimasukkan:
Saya bangga dengan beberapa konstruktor string kueri karena mereka bekerja tanpa fungsi array yang berat seperti array_merge. Terutama vsprintf () adalah temuan yang bagus.
Akhirnya saya perlu menambahkan 2x while () untuk menghindari melebihi batas memori. Ini tergantung pada batas memori Anda, tetapi ini adalah solusi umum yang baik untuk menghindari masalah (dan memiliki 10 permintaan masih jauh lebih baik dari 10.000).
sumber
test.php
Database.php
sumber
Saya memiliki masalah yang sama dan ini adalah bagaimana saya menyelesaikan untuk diri saya sendiri, dan saya membuat fungsi untuk diri saya sendiri untuk itu (dan Anda dapat menggunakannya jika itu membantu Anda).
Contoh:
Masukkan ke negara (negara, kota) NILAI (Jerman, Berlin), (Perancis, Paris);
Jika insertMultipleData ($ table, $ multi_params) mengembalikan TRUE , data Anda telah dimasukkan ke database Anda.
sumber
Berdasarkan percobaan saya, saya menemukan bahwa pernyataan insert mysql dengan beberapa baris nilai dalam satu transaksi adalah yang tercepat.
Namun, jika data terlalu banyak maka
max_allowed_packet
pengaturan mysql mungkin membatasi insert transaksi tunggal dengan beberapa baris nilai. Karenanya, fungsi berikut akan gagal saat ada data yang lebih besar darimax_allowed_packet
ukuran mysql :singleTransactionInsertWithRollback
singleTransactionInsertWithPlaceholders
singleTransactionInsert
Yang paling berhasil dalam menyisipkan skenario data besar adalah
transactionSpeed
metode, tetapi menghabiskan waktu lebih banyak metode yang disebutkan di atas. Jadi, untuk menangani masalah ini, Anda dapat membagi data menjadi potongan yang lebih kecil dan memanggil satu transaksi tunggal beberapa kali atau memberikan kecepatan eksekusi dengan menggunakantransactionSpeed
metode.Ini penelitian saya
Hasil untuk 100.000 entri untuk tabel yang hanya berisi dua kolom adalah sebagai berikut
sumber
Ini berhasil untuk saya
sumber
Bagaimana dengan sesuatu yang seperti ini:
Gagasan di balik ini adalah untuk menggilir nilai array Anda, menambahkan "nomor id" ke setiap loop untuk tempat penampung pernyataan yang disiapkan Anda sementara pada saat yang sama, Anda menambahkan nilai ke array Anda untuk parameter yang mengikat. Jika Anda tidak suka menggunakan indeks "kunci" dari array, Anda bisa menambahkan $ i = 0, dan $ i ++ di dalam loop. Entah berfungsi dalam contoh ini, bahkan jika Anda memiliki array asosiatif dengan kunci bernama, itu tetap berfungsi asalkan kunci tersebut unik. Dengan sedikit kerja akan baik-baik saja untuk array bersarang juga ..
** Perhatikan bahwa substr menghapus variabel terakhir ruang $ dan sql, dan koma, jika Anda tidak memiliki ruang Anda perlu mengubahnya ke -1 daripada -2.
sumber
Sebagian besar solusi yang diberikan di sini untuk membuat kueri yang disiapkan lebih kompleks dari yang seharusnya. Menggunakan fungsi bawaan PHP Anda dapat dengan mudah membuat pernyataan SQL tanpa overhead yang signifikan.
Diberikan
$records
, array catatan di mana setiap catatan itu sendiri merupakan array yang diindeks (dalam bentukfield => value
), fungsi berikut akan menyisipkan catatan ke dalam tabel yang diberikan$table
, pada koneksi PDO$connection
, hanya menggunakan pernyataan tunggal yang disiapkan. Perhatikan bahwa ini adalah solusi PHP 5.6+ karena penggunaan argumen membongkar panggilan untukarray_push
:sumber