在動態建構 SQL、自動產生表單、資料匯出等場景中,程式化取得資料表欄位名稱是常見需求。本文介紹從傳統 mysql_* 函數到現代 PDO 的多種做法,並說明資訊架構查詢的標準用法。
mysql_* 函數原範例使用的 mysql_connect、mysql_list_fields 等函數 自 PHP 5.5.0 棄用,PHP 7.0.0 完全移除。新專案請務必改用 PDO 或 mysqli。
<?php
function getTableColumnsPDO(PDO $pdo, string $database, string $table): array {
$sql = "
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = :db
AND TABLE_NAME = :tbl
ORDER BY ORDINAL_POSITION
";
$stmt = $pdo->prepare($sql);
$stmt->execute([':db' => $database, ':tbl' => $table]);
return $stmt->fetchAll(PDO::FETCH_COLUMN); // 直接回傳一維陣列
}
// 使用範例
$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8mb4', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$columns = getTableColumnsPDO($pdo, 'testdb', 'users');
print_r($columns);
/*
Array
(
[0] => id
[1] => username
[2] => email
[3] => created_at
)
*/
?>
<?php
function getTableColumnsMySQLi(mysqli $mysqli, string $database, string $table): array {
$sql = "
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = ?
AND TABLE_NAME = ?
ORDER BY ORDINAL_POSITION
";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('ss', $database, $table);
$stmt->execute();
$result = $stmt->get_result();
$columns = [];
while ($row = $result->fetch_row()) {
$columns[] = $row[0];
}
return $columns;
}
?>
若已執行 SELECT * FROM table LIMIT 0,可從結果集取得欄位中繼資料:
<?php
$stmt = $pdo->query('SELECT * FROM users LIMIT 0');
$meta = [];
for ($i = 0; $i < $stmt->columnCount(); $i++) {
$meta[] = $stmt->getColumnMeta($i)['name'];
}
print_r($meta);
?>
限制:需先執行查詢、驅動程式支援度不一(MySQL 支援、PostgreSQL 需額外設定)、無法取得完整結構定義(型別、長度、預設值等)。
<?php
// PDO
$stmt = $pdo->query("DESCRIBE `users`");
$columns = $stmt->fetchAll(PDO::FETCH_COLUMN, 0); // Field 欄位
// 或
$stmt = $pdo->query("SHOW COLUMNS FROM `users`");
$columns = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
?>
<?php
function getTableSchema(PDO $pdo, string $database, string $table): array {
$sql = "
SELECT
COLUMN_NAME AS `name`,
COLUMN_TYPE AS `type`,
IS_NULLABLE AS `nullable`,
COLUMN_DEFAULT AS `default`,
COLUMN_KEY AS `key`,
EXTRA AS `extra`,
COLUMN_COMMENT AS `comment`
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = :db
AND TABLE_NAME = :tbl
ORDER BY ORDINAL_POSITION
";
$stmt = $pdo->prepare($sql);
$stmt->execute([':db' => $database, ':tbl' => $table]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 輸出範例
/*
Array
(
[0] => Array
(
[name] => id
[type] => bigint(20) unsigned
[nullable] => NO
[default] =>
[key] => PRI
[extra] => auto_increment
[comment] => 主鍵 ID
)
[1] => Array
(
[name] => email
[type] => varchar(255)
[nullable] => NO
[default] =>
[key] => UNI
[extra] =>
[comment] => 使用者信箱
)
)
*/
?>
<?php
function buildInsertSQL(string $table, array $data, array $columns): string {
// 過濾資料中不存在於資料表的鍵
$fields = array_intersect_key($data, array_flip($columns));
if (empty($fields)) {
throw new InvalidArgumentException('無有效欄位資料');
}
$cols = implode('`, `', array_keys($fields));
$placeholders = ':' . implode(', :', array_keys($fields));
return "INSERT INTO `$table` (`$cols`) VALUES ($placeholders)";
}
// 使用
$columns = getTableColumnsPDO($pdo, 'testdb', 'users');
$userData = [
'username' => 'john',
'email' => 'john@example.com',
'invalid_field' => 'hack', // 會被自動過濾
];
$sql = buildInsertSQL('users', $userData, $columns);
$stmt = $pdo->prepare($sql);
$stmt->execute($userData); // PDO 自動綁定同名參數
?>
| 方法 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| INFORMATION_SCHEMA (PDO/mysqli) | 通用、需完整結構、跨資料庫 | 標準 SQL、資訊完整、支援參數綁定防注入 | 查詢較複雜、需權限存取 information_schema |
| DESCRIBE / SHOW COLUMNS | MySQL 專用、快速原型 | 語法簡短、直觀 | 非標準、難以參數化資料表名、資訊較少 |
| PDO::getColumnMeta | 已有查詢結果集、僅需欄位名 | 無額外查詢 | 需先查詢、驅動相依、資訊有限 |
建議採用 PDO + INFORMATION_SCHEMA 作為標準做法,兼具安全性、可攜性與資訊完整度。