
本文详解如何用 preg_split 安全分割包含注释、换行及语句内分号(如 COMMENT 中)的多条 MySQL 语句,避免误切,给出可直接运行的健壮正则方案与完整示例。
本文详解如何用 `preg_split` 安全分割包含注释、换行及语句内分号(如 comment 中)的多条 mysql 语句,避免误切,给出可直接运行的健壮正则方案与完整示例。
在处理数据库迁移脚本或批量 SQL 执行时,常需将一段包含多条语句的 SQL 文本按 ; 拆分为独立语句数组。但直接使用 explode(';', $sql) 或简单正则极易出错——因为 SQL 的 COMMENT、字符串字面量或函数调用中可能包含分号(如 '0=none; 1=test1'),导致错误截断。
核心挑战在于:仅匹配作为语句结束符的分号,而非出现在字符串、注释或括号内的分号。PHP 的 PCRE 支持 (*SKIP)(*F) 回溯控制动词,是解决此类“忽略上下文内分隔符”问题的理想工具。
以下为推荐的正则表达式方案:
$pattern = '~\([^)]*\)(*SKIP)(*F)|(?
-
\([^)]*\)(*SKIP)(*F):跳过所有圆括号内的内容(如DEFAULT '0=test; 1=test2;'中的分号),避免误匹配; (?:确保匹配位置前是分号;-
(?=\h*$|\s*\bALTER TABLE\b):正向先行断言,要求分号后紧跟:- 行尾空白(
\h*$,含空格、制表符等)→ 对应单句末尾; - 或空白后紧接下一个 SQL 语句关键字(如
ALTER TABLE)→ 处理连续语句(如...; ALTER TABLE ...);
- 行尾空白(
-
m修饰符启用多行模式,使^/$匹配每行首尾。
完整可运行示例:
$multiSql = "
ALTER TABLE `my_table` CHANGE `typ` `typ` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '0=none; 1=test1; 2=test2; 3=test3';
ALTER TABLE `my_table2`
ADD `date` varchar(25) COLLATE utf8_czech_ci DEFAULT NULL AFTER `test`;
ALTER TABLE `my_table3` ADD `date` varchar(25) COLLATE utf8_czech_ci DEFAULT NULL AFTER `test`; ALTER TABLE `my_table3` ADD `test2` varchar(25) COLLATE utf8_czech_ci DEFAULT NULL AFTER `date`;
ALTER TABLE `my_table3` CHANGE `test2` `test2` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '0=test; 1=test2;';
";
$sqlArray = array_map(
function($stmt) {
return trim(preg_replace('/\R/', ' ', $stmt)); // 将换行转为空格,保持语句整洁
},
preg_split($pattern, trim($multiSql), -1, PREG_SPLIT_NO_EMPTY)
);
// 输出验证
foreach ($sqlArray as $i => $sql) {
echo ($i + 1) . ". " . $sql . "\n";
}
✅ 输出结果(完全符合预期):
1. ALTER TABLE `my_table` CHANGE `typ` `typ` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '0=none; 1=test1; 2=test2; 3=test3'; 2. ALTER TABLE `my_table2` ADD `date` varchar(25) COLLATE utf8_czech_ci DEFAULT NULL AFTER `test`; 3. ALTER TABLE `my_table3` ADD `date` varchar(25) COLLATE utf8_czech_ci DEFAULT NULL AFTER `test`; 4. ALTER TABLE `my_table3` ADD `test2` varchar(25) COLLATE utf8_czech_ci DEFAULT NULL AFTER `date`; 5. ALTER TABLE `my_table3` CHANGE `test2` `test2` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '0=test; 1=test2;';
⚠️ 注意事项:
- 该正则专为
ALTER TABLE类语句优化,若需支持INSERT、CREATE、UPDATE等,建议将\bALTER TABLE\b替换为更通用的关键字组,例如\b(?:INSERT|UPDATE|DELETE|CREATE|DROP|ALTER)\b; - 未覆盖单引号/双引号字符串内的分号(如
'a;b'),如需极致鲁棒性,应使用专用 SQL 解析器(如php-sql-parser); - 始终对分割结果调用
trim()并清理换行,避免空格污染; - 生产环境建议添加异常处理,检测分割后是否为空或存在语法异常语句。
此方案平衡了简洁性与实用性,在绝大多数 MySQL 脚本场景下稳定可靠。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











