資料庫設計時使用 ENUM 類型限定欄位值(如狀態碼、分類),前端表單需要動態產生下拉選單選項,避免硬編碼導致資料庫與程式碼不同步。
SHOW COLUMNS FROM `table_name` LIKE 'enum_column_name'
回傳結果中 Type 欄位格式為:enum('value1','value2','value3')
<?php
/**
* 獲取 MySQL ENUM 欄位所有可能值
* @param PDO $pdo 資料庫連線
* @param string $table 資料表名稱
* @param string $column 欄位名稱
* @return array 列舉值陣列
* @throws Exception
*/
function getEnumValues(PDO $pdo, string $table, string $column): array {
// 防止 SQL 注入:驗證表名、欄位名格式
if (!preg_match('/^[a-zA-Z0-9_]+$/', $table) || !preg_match('/^[a-zA-Z0-9_]+$/', $column)) {
throw new InvalidArgumentException('無效的表名或欄位名');
}
$sql = "SHOW COLUMNS FROM `$table` LIKE ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$column]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
throw new Exception("找不到欄位:$table.$column");
}
$type = $row['Type']; // 例:enum('Y','N','P')
// 解析 enum 定義
if (!preg_match("/^enum\((.+)\)$/i", $type, $matches)) {
throw new Exception("欄位 $column 非 ENUM 類型");
}
$enumList = $matches[1]; // 'Y','N','P'
// 移除引號並分割
$values = array_map(function($v) {
return trim($v, "'\" "); // 移除單雙引號與空白
}, explode(',', $enumList));
return $values;
}
// ===== 使用範例 =====
try {
// 資料庫連線設定
$dsn = 'mysql:host=localhost;dbname=test_db;charset=utf8mb4';
$pdo = new PDO($dsn, 'username', 'password', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
// 獲取使用者表狀態欄位的 enum 值
$statusValues = getEnumValues($pdo, 'users', 'status');
// 輸出 HTML 選單
echo '<select name="status">';
foreach ($statusValues as $value) {
$label = match($value) {
'A' => '啟用',
'I' => '停用',
'P' => '審核中',
default => $value,
};
echo "<option value=\"$value\">$label</option>";
}
echo '</select>';
// 或輸出 JSON 供前端 AJAX 使用
header('Content-Type: application/json');
echo json_encode($statusValues);
} catch (Exception $e) {
error_log($e->getMessage());
http_response_code(500);
echo json_encode(['error' => '無法獲取列舉值']);
}
?>
ENUM 定義極少變更,建議快取減少資料庫查詢:
<?php
function getEnumValuesCached(PDO $pdo, string $table, string $column): array {
$cacheKey = "enum_values_{$table}_{$column}";
// 假設使用 Redis 快取
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
if ($redis->exists($cacheKey)) {
return json_decode($redis->get($cacheKey), true);
}
$values = getEnumValues($pdo, $table, $column);
$redis->set($cacheKey, json_encode($values), 86400); // 快取 24 小時
return $values;
}
?>
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| ENUM + 動態查詢 | 資料庫層級約束、儲存空間小 | 修改需 ALTER TABLE、跨資料庫移植性差 | 固定不變的分類(性別、狀態) |
| 查找表 + 外鍵 | 彈性擴充、標準化、易維護 | 需 JOIN 查詢、略佔空間 | 可能擴充的分類(商品類別、地區) |
| 前端硬編碼 | 簡單、無額外查詢 | 易不同步、維護困難 | 極少變更、極簡單專案 |
SHOW COLUMNS 權限Default 欄位取得預設值<?php
function getColumnDefinition(PDO $pdo, string $table, string $column): array {
$sql = "SHOW COLUMNS FROM `$table` LIKE ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$column]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
}
// 回傳陣列結構:
// [
// 'Field' => 'status',
// 'Type' => "enum('A','I','P')",
// 'Null' => 'NO',
// 'Key' => '',
// 'Default' => 'P',
// 'Extra' => ''
// ]
?>