Có thể chuẩn bị một truy vấn câu lệnh chèn hàng loạt bằng cách xây dựng nó ngay lập tức, nhưng cần một vài thủ thuật. Các bit quan trọng nhất đang sử dụng str_pad()
để tạo một chuỗi truy vấn có độ dài thay đổi và sử dụng call_user_func_array()
để gọi bind_param()
với một số tham số thay đổi.
function insertBulkPrepared($db, $table, $fields, $types, $values) {
$chunklength = 500;
$fieldcount = count($fields);
$fieldnames = '`'.join('`, `', $fields).'`';
$prefix = "INSERT INTO `$table` ($fieldnames) VALUES ";
$params = '(' . str_pad('', 3*$fieldcount - 2, '?, ') . '), ';
$inserted = 0;
foreach (array_chunk($values, $fieldcount*$chunklength) as $group) {
$length = count($group);
if ($inserted != $length) {
if ($inserted) $stmt->close();
$records = $length / $fieldcount;
$query = $prefix . str_pad('', 3*$length + 2*($records - 1), $params);
#echo "\n<br>Preparing '" . $query . "'";
$stmt = $db->prepare($query);
if (!$stmt) return false;
$binding = str_pad('', $length, $types);
$inserted = $length;
}
array_unshift($group, $binding);
#echo "\n<br>Binding " . var_export($group, true);
$bound = call_user_func_array(array($stmt, 'bind_param'), $group);
if (!$bound) return false;
if (!$stmt->execute()) return false;
}
if ($inserted) $stmt->close();
return true;
}
Hàm này lấy $db
của bạn dưới dạng mysqli
ví dụ, một tên bảng, một mảng tên trường và một mảng phẳng các tham chiếu đến các giá trị. Nó chèn tối đa 500 bản ghi cho mỗi truy vấn, sử dụng lại các câu lệnh đã chuẩn bị khi có thể. Nó trả về true
nếu tất cả các lần chèn thành công hoặc false
nếu bất kỳ trong số họ không thành công. Lưu ý:
- Tên bảng và trường không được thoát; Tôi để nó cho bạn để đảm bảo rằng chúng không chứa dấu gạch ngược. May mắn thay, chúng không bao giờ đến từ thông tin người dùng nhập.
- Nếu độ dài của
$values
không phải là bội số chẵn của độ dài$fields
, đoạn cuối có thể sẽ không thành công ở giai đoạn chuẩn bị. - Tương tự, độ dài của
$types
tham số phải khớp với độ dài của$fields
trong hầu hết các trường hợp, đặc biệt là khi một số khác nhau. - Nó không phân biệt giữa ba cách thất bại. Nó cũng không theo dõi số lần chèn thành công, cũng như không cố gắng tiếp tục sau khi có lỗi.
Với chức năng này được xác định, mã mẫu của bạn có thể được thay thế bằng một cái gì đó như:
$inserts = array();
for ($j = 0; $j < $abilitiesMax - 2; $j++) {
$inserts[] = &$abilityArray[$i]['match_id'];
$inserts[] = &$abilityArray[$i]['player_slot'];
$inserts[] = &$abilityArray[$i][$j]['ability'];
$inserts[] = &$abilityArray[$i][$j]['time'];
$inserts[] = &$abilityArray[$i][$j]['level'];
}
$fields = array('match_id', 'player_slot', 'ability', 'time', 'level');
$result = insertBulkPrepared($db, 'abilities', $fields, 'iiiii', $inserts);
if (!$result) {
echo "<p>$db->error</p>";
echo "<p>ERROR: when trying to insert abilities query</p>";
}
Các dấu và đó là quan trọng, vì mysqli_stmt::bind_param
yêu cầu tham chiếu, không được cung cấp bởi call_user_func_array
trong các phiên bản PHP gần đây.
Bạn đã không cung cấp cho chúng tôi báo cáo đã chuẩn bị ban đầu, vì vậy bạn có thể cần phải điều chỉnh tên bảng và trường. Có vẻ như mã của bạn nằm trong một vòng lặp qua $i
; trong trường hợp đó, chỉ for
vòng lặp cần phải ở bên trong vòng lặp bên ngoài. Nếu bạn lấy các dòng khác bên ngoài vòng lặp, bạn sẽ sử dụng nhiều bộ nhớ hơn một chút để xây dựng $inserts
mảng, đổi lại cho số lượng lớn chèn hiệu quả hơn nhiều.
Cũng có thể viết lại insertBulkPrepared()
để chấp nhận một mảng nhiều chiều, loại bỏ một nguồn lỗi tiềm ẩn, nhưng điều đó đòi hỏi phải làm phẳng mảng sau khi phân loại nó.