正则表达式,用于正确拆分嵌套的SQL分隔符

时间:2015-08-21 20:50:38

标签: php mysql regex preg-replace

我正在处理包含多个SQL查询的字符串:

ALTER TABLE _version ADD test1 INT NOT NULL;
ALTER TABLE _version ADD test2 INT NOT NULL;
CREATE PROCEDURE test3 ()
        LANGUAGE SQL
        DETERMINISTIC
        SQL SECURITY DEFINER
        COMMENT 'A procedure'
        BEGIN
            SELECT 'Hello World !';
        END;

我希望将此字符串拆分为三个单独查询的数组,如下所示:

ALTER TABLE _version ADD test1 INT NOT NULL;

ALTER TABLE _version ADD test5 INT NOT NULL;

CREATE PROCEDURE test3 ()
        LANGUAGE SQL
        DETERMINISTIC
        SQL SECURITY DEFINER
        COMMENT 'A procedure'
        BEGIN
            SELECT 'Hello World !';
        END;

这将允许我使用PDO进行多个查询。因为不幸(据我所知),您不能使用PDO从一个语句中进行多个事务查询。那,你不能在PDO查询中使用DELIMITER $$ SQL命令。

但是,请注意;BEGIN之间的END分隔符。这导致了问题!如果我只是使用;分隔符来爆炸字符串,它就不会正确地分离出过程。

我一直在使用正则表达式来查找BEGINEND之间的所有内容,然后用其他内容替换这些分隔符(例如$$分隔符) - 然后从$$分隔符进行爆炸 - 但似乎没有任何工作。

这是对我在PHP和正则表达式中尝试过的一个(坏)的抨击,其中$sqlString是包含多个查询的字符串:

$sqlString = preg_replace("#(?<!BEGIN.+);(?!.+END)#",'$$',$sqlString);
$splitQueries = explode("$$",$sqlString);

但我不能让负面的前瞻/后视工作。请帮我弄清楚正确的正则表达式或正确的方向!

1 个答案:

答案 0 :(得分:2)

免责声明:这完全符合您的要求并在;之后拆分输入,除非在BEGIN / END语句之间(会因WHERE column = ';'

等问题而失败

这个PHP语句将完全按照您的要求执行(RegEx demo):

$splitQueries = preg_split('/(?<=;)(?!(?:(?!BEGIN).)*END)/s', $sqlString);
array_pop($splitQueries); // there is an extra value in the array, assuming
                          // your query ends in ; 

首先,我使用了分号的正向lookbehind,这样你就可以使用一个简单的preg_split()调用(因为它实际上不会匹配;,而是它后面的空格)。

接下来,我通过否定前瞻跟踪:(?!.*END)。这是s修饰符所在的位置,因为它使.与换行符匹配。

最后,我将否定前瞻中的.*替换为另一个否定前瞻:(?:(?!BEGIN).)*

我们的最终结果是查找分号(技术上紧跟在后面的空格),然后查看每个后续字符(除非我们看到BEGIN)以确保没有END(信号)我们在BEGIN / END声明中的事实。有趣!