位置:首页 > PHP > PHP中按分号分割含嵌套分号的多条SQL语句方法

PHP中按分号分割含嵌套分号的多条SQL语句方法

时间:2026-08-18  |  作者:骑光打字机  |  阅读:0

本文介绍一种健壮的正则方案,利用 (*SKIP)(*F) 跳过字符串字面量和注释中的分号,精准按语义分号分割多条 SQL 语句,尤其适用于含 COMMENT '...;...' 等嵌套分号的复杂场景。

如何在 PHP 中正确使用正则表达式按分号分割含嵌套分号的多条 SQL 语句

在 PHP 中拆分多条 SQL 语句时,不能直接按分号切分。

处理数据库迁移脚本,或者批量执行 SQL 时,经常会遇到一个需求:把一整段包含多条语句的 SQL 字符串,拆成按语句划分的数组。

比如 .sql 文件里的内容,看上去只要用 explode(';', $sql) 就能完成。实际上,这种方式很容易出错。

因为 SQL 的注释,以及字符串字面量本身就可能带分号。例如 COMMENT '0=none; 1=test;'。这会导致语句被误切开,结果自然不准确。

推荐方案

PHP 的 preg_split() 结合 PCRE 的控制动词 (*SKIP)(*F),是解决该问题的推荐方式。

它可以“跳过”括号、单引号、双引号等界定符内的所有内容,只在外部真正的语句结束处分割。

完整解决方案

以下为经过验证的完整解决方案:

$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;';
";

// 核心正则:跳过括号内内容,匹配语句末尾的分号(后接空白行或下一个语句关键字)
$pattern = '~( [^)]* )(*SKIP)(*F) | ' [^']* '(*SKIP)(*F) | " [^"]* "(*SKIP)(*F) | (<=;)(=h*$|s*b(ALTER|CREATE|INSERT|UPDATE|DELETE|DROP|REPLACE|SELECT)b)~x';

$sqlArray = preg_split($pattern, trim($multiSql), -1, PREG_SPLIT_NO_EMPTY);

// 清理每条语句:合并换行、去除首尾空白
$sqlArray = array_map(function($stmt) {
return trim(preg_replace('/s+/', ' ', $stmt));
}, $sqlArray);

print_r($sqlArray);

输出效果

输出效果:共 5 条有效语句。

Array
(
[0] => ALTER TABLE `my_table` CHANGE `typ` `typ` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '0=none; 1=test1; 2=test2; 3=test3';
[1] => ALTER TABLE `my_table2` ADD `date` varchar(25) COLLATE utf8_czech_ci DEFAULT NULL AFTER `test`;
[2] => ALTER TABLE `my_table3` ADD `date` varchar(25) COLLATE utf8_czech_ci DEFAULT NULL AFTER `test`;
[3] => ALTER TABLE `my_table3` ADD `test2` varchar(25) COLLATE utf8_czech_ci DEFAULT NULL AFTER `date`;
[4] => ALTER TABLE `my_table3` CHANGE `test2` `test2` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '0=test; 1=test2;';
)

关键说明

  • 正则中 (*SKIP)(*F) 组合确保匹配到括号 (...)、单引号 '...' 或双引号 "..." 内容时立即回溯并跳过,避免误切内部的分号。
  • (<=;) 是肯定性后行断言,要求分割点前必须是分号。
  • (=h*$|s*b(ALTER|...)b) 是前瞻性检查:分号后必须是“纯空白至行尾”或“空白 + 下一条 SQL 关键字”,从而确保只在语句边界分割。
  • x 修饰符启用正则注释与空白忽略,提升可读性。实际使用时可移除换行与空格,以适配单行模式。
  • array_map() 后处理统一规范化语句格式,可压缩多余空格并清理换行,提升可执行性与可读性。

注意事项

  • 本方案默认支持常见 DDL/DML 关键字(ALTER, CREATE, INSERT 等)。如需支持 WITH, BEGIN, DO 等,需扩展关键词列表。
  • 不处理三重引号(如 MySQL 的反引号嵌套)或转义字符(如 'O'Reilly')。生产环境建议优先使用专业 SQL 解析器,如 php-sql-parser
  • 若 SQL 中存在无关键字开头的语句,如纯 SELECT ...; 后紧跟 ;,请确保前瞻条件覆盖完整。

结论

掌握此正则逻辑,即可安全、准确地解析绝大多数手工编写的多语句 SQL 脚本,为自动化数据库部署打下坚实基础。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多