最直接方式是用 mysqli_multi_query() 一次性执行分号分隔的多条 CREATE TABLE 语句;PDO 则需循环调用 exec()。注意:DDL 不支持事务回滚,需显式加反引号防关键字冲突,并避免空行注释。
mysqli 或 PDO 批量执行建表语句最直接PHP 本身不提供“批量建表”函数,本质是拼好多个 CREATE TABLE
SQL 语句,再一次性或逐条执行。关键在控制执行方式:一次连接、一次 mysqli_multi_query() 最快;用 PDO::exec() 循环执行次之,但更易捕获单条失败。
mysqli_multi_query() 支持用分号分隔的多条语句,但注意它不返回每条语句的详细错误,出错时需用 mysqli_next_result() 和 mysqli_error() 逐个检查PDO 默认不支持多语句执行(PDO::ATTR_EMULATE_PREPARES = true 也不行),必须循环调用 exec()
mysqli_multi_query() 对格式敏感)手写一堆 CREATE TABLE t1 (...); CREATE TABLE t2 (...); 容易漏逗号、引号或引擎参数。推荐用数组定义结构,动态生成 SQL:
$tables = [
'logs_202501' => ['id' => 'BIGINT PRIMARY KEY', 'msg' => 'TEXT'],
'logs_202502' => ['id' => 'BIGINT PRIMARY KEY', 'msg' => 'TEXT'],
];
$sqls = [];
foreach ($tables as $name => $fields) {
$fieldDefs = implode(', ', array_map(
fn($col, $def) => "`$col` $def",
array_keys($fields),
$fields
));
$sqls[] = "CREATE TABLE IF NOT EXISTS `$name` ($fieldDefs) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
}
$batchSql = implode('; ', $sqls); // 注意:mysqli_multi_query 要求严格分号结尾,无空格干扰
`order`)IF NOT EXISTS 可避免重复建表报错,但不会提示“已存在”,需自行判断是否真要跳过建表(CREATE TABLE)是 DDL 操作,MySQL 中会隐式提交当前事务,所以把几十个 CREATE 包进 BEGIN/COMMIT 完全无效——中间任一失败,前面建好的表也不会回滚。
START TRANSACTION 包裹建表语句USE db_name),避免每条语句都带库名前缀MySQL 5.7 及以前,CREATE TABLE 会持有 metadata lock (MDL),如果同时跑多个建表请求,后到的会被阻塞,表现为卡住几秒甚至几十秒。MySQL 8.0 改进了 MDL,但仍可能争用。
立即学习“PHP免费学习笔记(深入)”;
SHOW PROCESSLIST 观察状态是否卡在 Waiting for table metadata lock
mysql -u root -p dbname 命令行执行,由 MySQL Server 单线程顺序处理
实际批量建表的瓶颈往往不在 PHP,而在磁盘 I/O 和 MySQL 的 frm / ibd 文件初始化。别迷信“一次发 100 条”,测出来 20 条并行反而更稳,才是真实场景。