PHP中建库并导入SQL需分两步:先执行CREATE DATABASE(显式指定字符集),再重新连接该库;推荐用file_get_contents()+逐条mysqli_query()执行,避免multi_query缺陷,或用shell_exec("mysql")调用命令行工具,注意密码安全与错误检测。

用 mysqli 或 PDO 执行 CREATE DATABASE 后立刻导入 SQL 文件
PHP 本身不提供“一键建库+导入数据”函数,必须分两步:先建库,再连接该库并执行 SQL 语句。常见错误是建库后没切换数据库连接,导致 mysqli_query() 报错 No database selected。
实操建议:
- 用
mysqli::query()或PDO::exec()执行CREATE DATABASE `mydb` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci(显式指定字符集,避免后续乱码) - 建库成功后,**必须重新用新库名初始化连接**(PDO)或调用
mysqli_select_db()(mysqli),不能复用原连接 - 读取 SQL 文件推荐用
file_get_contents()+mysqli_multi_query(),不要用mysql_query()(已废弃)或逐行explode(';', $sql)(会误切注释和字符串内的分号)
处理含多条语句的 SQL 文件时避免 mysqli_multi_query() 卡死
mysqli_multi_query() 虽支持批量执行,但不会自动跳过空行、注释或 DELIMITER 变更,遇到存储过程或触发器定义容易中断,且无法获取每条语句的错误详情。
更稳妥的做法是预处理 SQL 内容:
立即学习“PHP免费学习笔记(深入)”;
- 用正则剔除
--和/*...*/注释(注意避开字符串内出现的--) - 按
;分割语句前,先用状态机跳过引号包裹的内容和括号嵌套(简单场景可用preg_split('/;(?!.*[\'"])/', $sql)粗略处理) - 对每条非空语句单独调用
mysqli_query(),便于定位哪句失败(例如建表语句里字段类型写成INT(11)在 MySQL 8.0.19+ 会警告但不报错,而JSON字段在旧版本直接报错)
用 shell_exec() 调用 mysql 命令行工具最省事但要注意权限和路径
绕过 PHP SQL 解析,直接走系统命令导入,适合大文件或含复杂语法的 SQL(如 LOAD DATA INFILE)。但必须确认:mysqld 进程允许本地 socket 连接、PHP 进程有权限读取 SQL 文件、mysql 命令在 PATH 中。
典型命令写法:
shell_exec("mysql -u root -prootpass -h 127.0.0.1 mydb < /path/to/init.sql 2>&1");
关键点:
- 密码写在命令行里有泄露风险(
ps aux可见),应改用配置文件:mysql --defaults-extra-file=/tmp/my.cnf mydb ,其中/tmp/my.cnf权限设为600 - 如果 MySQL 配置了
skip-networking,-h 127.0.0.1会强制走 TCP,需改为-S /var/run/mysqld/mysqld.sock指定 socket 路径 - 返回值为空不代表成功,要检查
shell_exec()输出是否含ERROR或Warning字样
初始化数据时外键约束和自增 ID 的坑
SQL 文件若含 INSERT INTO user VALUES (1,'a'),(2,'b'),而表已有数据,会导致主键冲突;若含 SET FOREIGN_KEY_CHECKS=0 但结尾没恢复,后续操作可能意外破坏参照完整性。
安全做法:
- 导入前加
SET FOREIGN_KEY_CHECKS=0; SET UNIQUE_CHECKS=0; SET AUTOCOMMIT=0;,导入后补SET FOREIGN_KEY_CHECKS=1; SET UNIQUE_CHECKS=1; COMMIT; - 对含自增字段的表,在
INSERT前加TRUNCATE TABLE或DELETE FROM+ALTER TABLE ... AUTO_INCREMENT = 1,否则新数据 ID 可能从高位开始 - 如果 SQL 文件由
mysqldump --no-create-info生成,它默认不带SET语句,必须手动补全或用--skip-extended-insert避免单条语句过长触发 max_allowed_packet
CREATE DATABASE 和后续导入最好包在事务外——因为建库操作本身不能回滚,但数据导入失败时得能清理掉空库,这点容易被忽略。











