【问题标题】:How can I separate a list of SQL queries from a file?如何从文件中分离 SQL 查询列表?
【发布时间】:2010-08-27 23:39:06
【问题描述】:

如果所有查询都以; 结尾,我可以通过这个字符爆炸,但是当; 出现在字段中时我该怎么办?

例如

[...]Select * From my_data where idk=';';\nSelect [...]  

[...]Select * From my_data where idk=';\n';Select [...]

我的文件包含各种查询,包括 INSERTs,并且可以有类似上面所示的语法变化,其中 ; 后面有时会在字段内换行。

如何处理这个问题?

explode 之类的 PHP 函数会失败,eregipreg_match 会起作用吗?

【问题讨论】:

  • 都是select语句吗?
  • 我试图思考为什么这比使用 MySQL 函数和/或存储过程更好......
  • 因为问题是 ';' 您可以用特殊字符或类似字符替换它们,然后分解文件..

标签: php mysql regex


【解决方案1】:

我建议编写一个非常简单的解析器。解析器像状态机一样工作,状态机将对字符进行操作。基本上,以下状态机将吃掉字符,直到找到不在由单引号分隔的字段内的;

// no guarantees this is a fast or efficient one-liner
// PHP isn't the the greatest language for this sort of thing
$chars = str_split(implode("\n", file('filename.txt')));
$state = 0; // 0 = not in field, 1 = in field, 2 = in field, escaped char    
$query = "";
// loop over all characters in the file
foreach($c in $chars){
    // no matter what, append character to current query
    $query .= $c;

    // now for the state machine
    switch( $state ){
        case 0:
            if( $c == "'" ){
               $state = 1;
            }else if( $c == ";" ){
               // have a full query, do something with it
               // say, write $query to file
               // now reset $query
               $query = "";
            }
            break;
        case 1:
            if( $c == "'" ){
                // if the current character is an unescaped single quote
                // we have exited this field (so back to state 0)
                $state = 0;
            }else if( $c == "\\" ){
                // we found an backslash and so must temporarily
                // sit in a different state (avoids the sequence \')
                // and deals appropriately with \\'
                $state = 2;
            }
            break;
        case 2:
            // we can escape any char, to get here we were in a field
            // so to a field we must return
            $state = 1;
    }
}

【讨论】:

  • fopen => fread => and hmm 'foreach( char c in file ){' ? c ) $last_c 是最后一个字符吗?顺便提一句。求帮助
  • @Programmer,现在都是 PHP 了。
【解决方案2】:

实际语句并不多,那么如果您将\s*statement 爆炸但添加了所有语句怎么办?

示例...

$queries = <<<END
select * from table where text=";";


insert into table(adsf,asdf,fff) values(null,'text;','adfasdf');
update table set this="adasdf;";

alter table add column;
select * from table where text=";";


insert into table(adsf,asdf,fff) values(null,'text;','adfasdf');
update table set this="adasdf;";

alter table add column;
END;

$statements = preg_split( '~;\s*(select.*?)|;\s*(insert.*?)|;\s*(update.*?)|;\s*(alter.*?)~i', $queries, null, PREG_SPLIT_DELIM_CAPTURE );

$good_statements[] = array_shift( $statements );

foreach( $statements as $statement ) {

    if ( $statement == '' ) continue;
    if ( !($i % 2) ) {
        $statement_action = $statement;
        echo $statement;
        $i++;
    }
    else {
        $good_statements[] = sprintf( "%s %s", trim( $statement_action ), trim( $statement ) );
        $i++;
    }

}


print_r($good_statements);

【讨论】:

  • hmm END 后的空格:和
【解决方案3】:

可靠且普遍?您必须编写一个解析器,该解析器不仅理解一般的 SQL,而且尤其理解 MySQL 的非标准 SQL 变体的复杂性。例如,它必须应对:

  • 具有\-escapes 的单引号字符串,因此\' 不会结束字符串(但\\' 会),除非使用了NO_BACKSLASH_ESCAPE sql_mode 选项;
  • 双引号字符串是字符串文字,具有类似的转义规则,除非 ANSI_QUOTES 已打开;
  • 反引号架构名称(双引号应该做什么);
  • /* ... */#-- 这样古怪的 cmets 不会开始评论,除非前面有空格

这是一个巨大的痛苦,这就是为什么通常最好避免将多个 SQL 语句粘在一起。

【讨论】:

  • 为什么需要通用解析器?一些简单的东西可以处理输入和退出字段,; 在语法中的其他任何地方都是不合法的,因此只要您能够充分确定何时在字符串中以及何时不在,它就可以很好地界定。
  • 由于上面列出的原因,确定您是否在字符串文字(或注释或模式名称文字,; 也可以存在)中很棘手。你的解析器示例需要一堆额外的状态来处理这个问题,如果它对于一般情况是可靠的,你还需要开关来确定 sql_mode 和字符集(以避免臭名昭著的多字节字符集问题)。
猜你喜欢
  • 2011-10-12
  • 2016-01-11
  • 2021-09-02
  • 1970-01-01
  • 2018-12-19
  • 2019-05-27
  • 1970-01-01
  • 1970-01-01
  • 2017-04-16
相关资源
最近更新 更多