【问题标题】:Regular expression to get selected columns获取选定列的正则表达式
【发布时间】:2013-04-06 19:55:27
【问题描述】:

我必须从 MYSQL SELECT 中提取列名,我希望使用正则表达式来做到这一点。
这是一个普通的SELECT,类似于:
SELECT column1, column2 ... FROM table

我必须涵盖所有情况,我们没有别名,前面有或没有表格,有或没有引用字符:

SELECT column, column as foo, table.column, table.column as foo, 
       `column`, `column` as foo, `table`.`column`, `table`.`column` as foo
       .....

目前我已经能够计算出这个正则表达式:#\w+(\sas)?#i,但这与前缀列相比并不好。
有什么帮助吗?

顺便问一下,Regex 擅长这个任务吗?

编辑
感谢您的回答!
您发布的模式对整个查询都有效,实际上我已经在处理每一列:

$fields = Frameworkmethod::getSelectFields($query);
$columns = explode(',' , $fields);
foreach($columns as $column)
{
     //do Regex work to "clean up" the single field and get the "standard" one (not the alias)
     //`#__tracktime_projects`.`pr_name` AS `project_name` should return pr_name
}

如上面评论中所述,我总是需要字段名称,而不是别名。 很抱歉之前没有指出!

【问题讨论】:

  • is Regex good at this task 这完全取决于您用于编程的语言!指定您使用的语言/工具,以便我们为您提供帮助

标签: php mysql regex


【解决方案1】:

我使用了Collapse and Capture a Repeating Pattern in a Single Regex Expression 并对其进行了调整以适应此目的。

因此,希望是防弹的正则表达式,用于从 *SQL 查询中捕获列名:

/(?:SELECT\s++(?=(?:[#\w,`.]++\s++)+)|(?!^)\G\s*+,\s*+(?:`?+\s*+[#\w]++\s*+`?+\s*+\.\s*+)?+`?+\s*+)(\w++)`?+(?:\s++as\s++[^,\s]++)?+/ig

解说在线演示:http://regex101.com/r/wL7yA9

使用带有单个正则表达式的 preg_match_all() 的 PHP 代码,用 /x 修饰符注释:

preg_match_all('/(?:SELECT\s++(?=(?:[\#\w,`.]++\s++)+) # start matching on SELECT
                |              # or
                (?!^)\G        # resume from last match position 
                \s*+,\s*+      # delimited by a comma 
                (?:`?+\s*+     # optional prefix table with optional backtick
                    [\#\w]++   # table name
                    \s*+`?+    # optional backtick
                    \s*+\.\s*+ # dot separator
                )?+ # optional prefix table end group

                `?+\s*+ # optional backtick

            ) # initial match or subsequent match

            (\w++)    # capturing group
            `?+         # optional backtick


            (?:\s++as\s++[^,\s]++)?+ # optional alias

            /ix', $query, $matches);

直播代码:http://codepad.viper-7.com/VTaPd3

注意:“希望是防弹的”是针对有效的 SQL


使用explode()的PHP代码

$columns = explode(',', $fields);

foreach($columns as $column)
{
    $regex='/([\w]++)`?+(?:\s++as\s++[^,\s]++)?+\s*+(?:FROM\s*+|$)/i';

    preg_match($regex, $column, $match);

    print $match[1]; // field stored in $match[1]
}

带有示例提取的实时代码:http://codepad.viper-7.com/OdUGXd

【讨论】:

    【解决方案2】:

    我用过 PHP:

    $query = 'SELECT column1, column2 as foo, table.column3, table.column4 as foo, 
           `column5`, `column6` as foo, `table`.`column7`, `table`.`column8` as foo
           FROM table';
    
    $query = preg_replace('/^SELECT(.*?)FROM.*$/s', '$1', $query); // To remove the "SELECT" and "FROM table..." parts
    
    preg_match_all('/(?:
        (?:`?\w+`?\.)? (?:`)?(\w+)(?:`)? (?:\s*as\s*\w+)?\s*
    #   ^--TableName-^ ^---ColumnName--^ ^----AsFoo-----^
    )+/x',$query, $m);
    
    print_r($m[1]);
    

    输出:

    Array
    (
        [0] => column1
        [1] => column2
        [2] => column3
        [3] => column4
        [4] => column5
        [5] => column6
        [6] => column7
        [7] => column8
    )
    

    现场演示:http://www.rubular.com/r/H960NFKCTr


    更新:由于您使用了一些“不寻常”但有效的 SQL 表名(例如:#__tracktime_projects),因此它弄乱了正则表达式。所以为了解决这个问题,我添加了一个包含我们期望的字符的变量,我还添加了i 修饰符以使匹配不区分大小写:

    $query = 'SELECT column1, column2 as foo, table.column3, table.column4 as foo, 
           `column5`, `column6` as foo, `table`.`column7`, `table`.`column8` as foo, `#__tracktime_projects`.`pr_name` AS project_name, `#wut`
           FROM table';
    
    
    $query = preg_replace('/^SELECT(.*?)FROM.*$/s', '$1', $query); // To remove the "SELECT" and "FROM table..." parts
    
    $allowed = '\w#'; // Adjust this to the names that you expect.
    
    preg_match_all('/(?:
        (?:`?['.$allowed.']++`?\.)?
    #   ^--------TableName--------^
    
        (?:`)?(['.$allowed.']++)(?:`)?
    #   ^----------ColumnName--------^
    
        (?:\s*as\s*['.$allowed.']++)?\s*
    #   ^-------------AsFoo------------^
    )+
    /xi',$query, $m);
    
    print_r($m[1]);
    

    输出:

    Array
    (
        [0] => column1
        [1] => column2
        [2] => column3
        [3] => column4
        [4] => column5
        [5] => column6
        [6] => column7
        [7] => column8
        [8] => pr_name
        [9] => #wut
    )
    

    现场演示:http://www.rubular.com/r/D0iIHJQwB8

    【讨论】:

    • 很好,但是我需要字段名而不是别名(抱歉,我刚刚更新了问题)
    • testing vs 这个字符串提取别名而不是列名:rubular.com/r/SJLbVC0GGR
    【解决方案3】:

    如果使用 PHP(我相信其他语言也支持),您可以考虑 getcolumnmeta。来自文档:

    <?php
      $select = $DB->query('SELECT COUNT(*) FROM fruit');
      $meta = $select->getColumnMeta(0);
      var_dump($meta);
    ?>
    

    结果:

    array(6) {
      ["native_type"]=>
       string(7) "integer"
      ["flags"]=>
       array(0) {
      }
      ["name"]=>
       string(8) "COUNT(*)"
      ["len"]=>
        int(-1)
      ["precision"]=>
        int(0)
      ["pdo_type"]=>
        int(2)
    }
    

    【讨论】:

      【解决方案4】:

      这是通用解决方案:

      ((select|,)\s+((((`)?\w+\6?\.)?(`)?\w+\7?)(\s+as\s+(`)?\w+\9?)?\s*))+from\s
      

      $3 组包含带有可能的表和别名装饰的列。

      您可以将非捕获组 - (?...) 用于表达式的某些部分。在这种情况下,请更改反向引用编号。

      用单行运行它并忽略大小写标志。也许您应该将标识符规范 \w+ 更改为更具体的 [a-zA-Z]\w* 例如。

      【讨论】:

      • 该工具似乎只报告了最后一次捕获。
      • 在您的验证器中仅使用 '(select|,)\s*((()?\w+\4?\.)?()?\w+\5?(\s+as\s+(`)?\w+\7 ?)?)\s*' 但带有 isg 选项。
      • 不太好。查看 regex101.com/r/rL1bE2。你认为这个结果正确吗?它应该列出所有'a,','b,'和'c,'。这就是为什么它没有在我的正则表达式中列出所有列的原因。
      • 让我们在RegEx chat room 上继续讨论,以免评论空间混乱,也请删除此处的 cmets
      【解决方案5】:

      这是一个老问题,但我遇到了同样的问题,无法使用 @CSᵠ 正则表达式解决。 我创建了另一个正则表达式来在更广泛的列上执行任务,包括计算字段

      preg_match_all('/(?<=^SELECT |, |\) )([a-z]+\.)?([a-z]+ )?(as )?([a-z]+)(?= ?,|$)/im');
      

      如果将用于复杂查询,我建议使用我创建的完整函数:https://gist.github.com/pedrosancao/2498ed85b3c1834c5bdd

      【讨论】:

        【解决方案6】:

        我迟到了,但其中大多数对我来说太复杂了,而且是 PHP 特有的,我想这就是你所追求的,但很多人可能不是。 我使用的 PCRE 风格的正则表达式是:

        ([\w`]+)(?=\s*,|\s+from\s+)
        

        这匹配紧跟逗号(或空格和逗号)或空格和关键字“from”的字母数字和反引号 ([\w`]+) 字符。

        后者是使用积极的前瞻来完成的

        (?=<expression>)
        

        在这种情况下

        (?=\s*,|\s+from\s+)
        

        检查前面的记号 [\w`]+ 是否跟在

        \s*,       # whitespace (0 or more) and comma
        

        或(|)

        \s+from\s+ # the keyword 'from', surrounded by >=1 whitespace characters
        

        然后您可以使用任何语言风格的大小写标志来忽略大小写。

        编辑:还应该注意这匹配别名而不是原始列名。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-04
          • 1970-01-01
          相关资源
          最近更新 更多