将多个SQL语句拆分为单独的SQL语句

时间:2009-03-11 01:43:43

标签: php sql mysql

简介:我希望有一个库或例程可以做到这一点,但我找不到这样的东西。我真的在寻找方向和建议从哪里开始......

情况如下: 我有一块SQL命令以纯文本形式出现。它可能是一个或几个SQL命令。我需要一种方法来分割多个SQL命令,这样我就可以一次运行一个。 Microsoft SQL Management Studio确实具有开箱即用的此行为。

我正在尝试将此功能添加到在Apache(Debian)上运行的PHP5 / MySQL5应用程序中。

一些要点:

  1. 我确实需要一次运行一个。严重。
  2. 我不想要求用户在每个SQL语句后输入一个分号。
  3. SQL语句可以在一行或多行上,因此我无法对LB / CR进行包装
  4. 至少需要支持SELECT,UPDATE,INSERT,DELETE。
  5. 它需要支持子选择的查询
  6. 整齐的标签式SQL需要工作
  7. (为了使用可用的软件)我不想强迫用户输入任何类型的分隔符。
  8. 以下是我需要分成两个语句的SQL示例块:

    select sMessage, 
    (
        SELECT COUNT(sTag) FROM Tags WHERE ixTicket = note.ixTicket
    ) FROM note
    select * from ticket
        WHERE (SELECT MAX(nCount) FROM Counter WHERE ixTicket = ticket.ixTicket) > 5
    

    我尝试了一些RegEx尝试,但这似乎不够强大。

    有关解决此问题的方法的任何建议吗?

10 个答案:

答案 0 :(得分:3)

我不确定这是否可行。您当然需要深入了解目标DBMS的SQL语法。例如,在我的脑海中,这是一个MySQL声明:

INSERT INTO things
SELECT * FROM otherthings ON DUPLICATE KEY
UPDATE thingness=thingness+1

在某些DBMS中可能存在一些构造,如果没有分隔符,可能会有歧义。

  

我不想要求用户在每个SQL语句后输入分号。

我想你可能会被迫。它完全是划分SQL语句的标准方法。即使您可以找到一个启发式来检测可能的SQL语句开始点,您也可能会发生意外“DELETE FROM things”-without-WHERE-clause等灾难。

  

SQL语句可以在一行或多行上,因此我无法在LB / CR上进行包装

是否可以接受双换行换新语句?

  

我尝试了一些RegEx尝试,但这似乎不够强大。

不,即使带有分号分隔符,正则表达式也远不足以解析SQL。问题点包括:

';'
";"
`;`
'\';'
''';'
-- ;
#;
/*;*/

以及这些结构的任何插入。 EEK!

答案 1 :(得分:1)

也许试试这个库。我以前成功地使用它来解析sql。 http://www.sqlparser.com/

答案 2 :(得分:1)

为定期导致问题的讨论添加一个怪癖:

DECLARE c CURSOR FOR
    SELECT * FROM SomeWhere ...
        FOR UPDATE

跟踪UPDATE往往会使特殊解析器失去优势。您可能不必担心这一点,因为首先不允许使用DECLARE表示法(实际上是嵌入式SQL,而不是纯SQL)。但是,即使不在DECLARE语句中,FOR UPDATE子句也可以出现在SQL的某些方言中,所以要小心。

答案 3 :(得分:1)

可能使用以下Java Regex?检查测试...

@Test
public void testRegexp() {
    String s = //
        "SELECT 'hello;world' \n" + //
        "FROM DUAL; \n" + //
        "\n" + //
        "SELECT 'hello;world' \n" + //
        "FROM DUAL; \n" + //
        "\n";

    String regexp = "([^;]*?('.*?')?)*?;\\s*";

    assertEquals("<statement><statement>", s.replaceAll(regexp, "<statement>"));
}

答案 4 :(得分:1)

    $sMultiQuery = 'SHOW TABLES; SELECT * FROM `test`';
    $aQueries    = array();

    if ( preg_match_all('/([^;]*?((\'.*?\')|(".*?"))?)*?(;\s*|\s*$)/', $sMultiQuery, $aMatches) )
    {
        $aQueries = $aMatches[0];
    }
    else
    {
        $aQueries = array($sMultiQuery);
    }

    foreach ( $aQueries as $sQuery )
    {
        # Do your thing
    }

答案 5 :(得分:0)

您最好的选择是要求用户在语句之间放置某种类型的分隔符。例如:要求每个语句用仅包含单词GO或“\”的行描述,或者用“;”结束每个语句。

通过这种方式,您可以轻松地将单个字符串分解为单独的SQL语句。

答案 6 :(得分:0)

如果您不希望用户输入分隔字符,例如';'或者其他任何事情,你需要自己解析输入并有逻辑来确定语句的开始位置。

你的逻辑需要处理显而易见的查询,启动关键字'SELECT','UPDATE','INSERT','DELETE'并向前推进到下一个关键字(或输入结束)。

答案 7 :(得分:0)

您是否尝试过使用关键字'SELECT','UPDATE','INSERT'和'DELETE'以及计算开头次数('和关闭大括号')?

这应该允许您确定避免嵌套的SELECT语句并找到语句的正确结尾。

答案 8 :(得分:0)

您需要使用分号分隔符。从技术上讲,没有它,SQL语句完全无效;任何省略它的人都在编写格式错误的SQL。要求分号以标准化的方式解决所有问题,并使软件易于编写。

或许执行以下操作:如果用户输入的查询包含一个或多个分号(当然在引号之外),请在末尾添加分号并将其作为单个查询运行。否则,以分号分隔输入的查询并单独运行每个查询,如果省略,可能在最终查询结尾处添加分号。

此解决方案易于编写,符合SQL标准,并且只是简单的工作。不要求分隔符是疯狂的必然途径。

答案 9 :(得分:-1)

我想你可以自己解析它。查找关键字SELECT,DELETE,UPDATE,INSERT,EXEC等

在解析时,如果遇到“(”增加计数器:nest_level ++

如果遇到“)”减少nest_level -

然后当你遇到一个关键字,并且nest_level == 0时,你就会进入下一个声明。

你还必须处理像

这样的案件
 INSERT ...
 SELECT ....

因此,对于INSERT,您必须寻找SELECT或VALUES ......

毫无疑问是其他情况。

同意kquinn你应该只需要分号。我不认为这有什么“不冷静”。