場景:插入 1000 個用戶。
方案 A:for 循環拼接 SQL → MySQL 重複編譯 1000 次相同語句結構。
方案 B:mysqli_multi_query 拼接 1000 條 → 單次發送大包,但解析優化仍重複。
方案 C:預處理 → prepare 一次 → 循環 bind_param + execute,MySQL 僅編譯優化一次。
<?php
$mysqli = new mysqli("localhost", "root", "", "test");
mysqli_query($mysqli, "SET NAMES utf8");
$stmt = $mysqli->prepare("SELECT id, username FROM `user` WHERE `id` > ?");
$id = 5;
$stmt->bind_param("i", $id); // 綁定輸入參數
$stmt->bind_result($id, $username); // 綁定輸出列
$stmt->execute();
while ($stmt->fetch()) {
echo "第 {$id} 個用戶: {$username}<br/>";
}
/* 此處可重複修改 $id → bind_param → execute
無需再次 prepare,複用編譯計劃 */
$stmt->free_result();
$stmt->close();
$mysqli->close();
?>
| 維度 | 普通查詢 | 預處理 |
|---|---|---|
| 編譯次數 | N 次 (N=執行次數) | 1 次 |
| 網絡傳輸 | 每次發送完整 SQL | 首次發送模板,後續僅發參數 |
| SQL 注入 | 需手動轉義 | 參數綁定天然免疫 |
| 類型安全 | 無 | bind_param 強類型檢查 |
$mysqli->begin_transaction();
$stmt = $mysqli->prepare("INSERT INTO log(msg) VALUES(?)");
$stmt->bind_param("s", $msg);
foreach ($messages as $msg) {
$stmt->execute();
}
$mysqli->commit();
$stmt->close();
CHECK TABLE、部分 DDL)bind_param 參數必須是變量引用,不能直接傳值/表達式max_allowed_packet 限制free_result() 避免內存洩漏