INSERT 基礎語法
-- 指定欄位插入(推薦,順序不綁定表結構)
INSERT INTO table_name (column1, column2, column3)
VALUES (value1, value2, value3);
-- 不指定欄位(需按表定義順序,不推薦生產用)
INSERT INTO table_name VALUES (value1, value2, value3);
-- 批次插入(單次 SQL,效能遠優於迴圈執行)
INSERT INTO table_name (col1, col2) VALUES
('a', 1), ('b', 2), ('c', 3);
-- 插入或更新(唯一鍵衝突時更新)
INSERT INTO table_name (id, name, email) VALUES (1, 'John', 'john@example.com')
ON DUPLICATE KEY UPDATE name=VALUES(name), email=VALUES(email);
PHP PDO 安全插入實作
單筆插入
<?php
$pdo = new PDO($dsn, $user, $pass, $options);
$stmt = $pdo->prepare("INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)");
$stmt->execute(['john_doe', 'john@example.com', password_hash('secret123', PASSWORD_DEFAULT)]);
$userId = $pdo->lastInsertId();
echo "新用戶 ID: $userId";
批次插入(交易 + 預處理)
<?php
$users = [
['alice', 'alice@example.com'],
['bob', 'bob@example.com'],
['charlie', 'charlie@example.com'],
];
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare("INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)");
foreach ($users as $u) {
$stmt->execute([$u[0], $u[1], password_hash('default_pass', PASSWORD_DEFAULT)]);
}
$pdo->commit();
echo "批次插入成功,共 " . count($users) . " 筆";
} catch (Exception $e) {
$pdo->rollBack();
throw $e;
}
HTML 表單處理插入(完整範例)
<?php
// insert.php
require_once 'config.php';
$errors = [];
$success = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// CSRF 驗證(略)
$firstName = trim($_POST['firstname'] ?? '');
$lastName = trim($_POST['lastname'] ?? '');
$age = (int)($_POST['age'] ?? 0);
if ($firstName === '') $errors[] = '請輸入名字';
if ($lastName === '') $errors[] = '請輸入姓氏';
if ($age < 1 || $age > 120) $errors[] = '年齡需在 1-120 之間';
if (empty($errors)) {
try {
$stmt = $pdo->prepare("INSERT INTO persons (first_name, last_name, age) VALUES (?, ?, ?)");
$stmt->execute([$firstName, $lastName, $age]);
$success = true;
// PRG 模式重導向
header('Location: insert.php?success=1');
exit;
} catch (PDOException $e) {
error_log("Insert error: " . $e->getMessage());
$errors[] = '資料庫錯誤,請稍後再試';
}
}
}
?>
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>新增人員</title></head><body>
<h1>新增人員記錄</h1>
<?php if ($success || isset($_GET['success'])): ?>
<div style="color:green">資料新增成功!</div>
<?php endif; ?>
<?php if ($errors): ?>
<ul style="color:red"><?php foreach($errors as $e) echo "<li>".htmlspecialchars($e)."</li>"; ?></ul>
<?php endif; ?>
<form method="POST">
<div><label>名字:<input type="text" name="firstname" required></label></div>
<div><label>姓氏:<input type="text" name="lastname" required></label></div>
<div><label>年齡:<input type="number" name="age" min="1" max="120" required></label></div>
<button type="submit">送出</button>
</form>
<p><a href="list.php">查看所有記錄</a></p>
</body></html>
關鍵安全與最佳實踐
| 實踐 | 說明 |
| 預處理語句 | prepare() + execute() 完全分離 SQL 邏輯與資料,防 SQL 注入 |
| 輸入驗證 | 型別檢查、長度限制、格式驗證(Email、電話等) |
| 輸出轉義 | HTML 輸出用 htmlspecialchars() 防 XSS |
| PRG 模式 | Post-Redirect-Get 避免重新整理重複提交 |
| 交易控制 | 批次操作用 beginTransaction/commit/rollBack 保證原子性 |
| 錯誤處理 | 捕捉 PDOException,記錄詳細錯誤,用戶端顯示友善訊息 |
| 字符集 | 連接 DSN 指定 charset=utf8mb4 |
常見錯誤避坑
- ❌ 直接拼接變數:
"INSERT ... VALUES ('$_POST[name]')" → SQL 注入漏洞
- ❌ 未驗證型別:年齡欄位存入字串導致資料髒汙
- ❌ 忽略唯一約束錯誤:重複 Email 導致 23000 錯誤碼未處理
- ❌ 自增 ID 依賴
mysql_insert_id() 舊函數 → 改用 $pdo->lastInsertId()