資料庫操作是 PHP 應用的核心,但錯誤的設計與存取模式往往導致安全漏洞、效能瓶頸與維護噩夢。本文剖析五大典型問題,並提供現代化的解決路徑。
mysql_* 函數風險:PHP 7.0 移除此擴展;無預處理語句,極易遭 SQL 注入;硬編碼資料庫類型,遷移困難。
// 錯誤示範
$db = mysql_connect('localhost', 'root', 'password');
mysql_select_db('users');
$res = mysql_query("SELECT id FROM users WHERE login='$name'");
解決方案:改用 PDO 或 MySQLi,啟用預處理語句:
// PDO 範例
$dsn = 'mysql:host=localhost;dbname=users;charset=utf8mb4';
$pdo = new PDO($dsn, 'root', 'password', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$stmt = $pdo->prepare('SELECT id FROM users WHERE login = ?');
$stmt->execute([$name]);
$user = $stmt->fetch();
現象:查詢緩慢,EXPLAIN 顯示 type: ALL。
對策:
WHERE、JOIN、ORDER BY 條件欄位建立索引。ANALYZE TABLE 更新統計資訊。WHERE YEAR(created_at) = 2023)。場景:迴圈中重複查詢關聯資料。
// 錯誤:N+1
$posts = $pdo->query('SELECT * FROM posts')->fetchAll();
foreach ($posts as $post) {
$author = $pdo->query("SELECT * FROM users WHERE id = {$post['user_id']}")->fetch();
}
// 正確:Eager Loading / JOIN
$posts = $pdo->query('
SELECT p.*, u.name AS author_name
FROM posts p
JOIN users u ON p.user_id = u.id
')->fetchAll();
風險:多表寫入局部失敗導致資料不一致。
$pdo->beginTransaction();
try {
$pdo->exec("INSERT INTO orders ... ");
$pdo->exec("UPDATE inventory SET qty = qty - 1 WHERE product_id = ?", [$pid]);
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
throw $e;
}
後果:難以測試、重構、審計。
建議:
mysql_* 函數EXPLAIN 驗證索引