我必须从MYSQL SELECT
中提取列名,我希望使用Regex来实现这一点。
这是一个简单的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
}
如上面的评论所述,我总是需要字段名称,而不是别名。 很抱歉没有在之前指出它!
答案 0 :(得分:7)
我利用了Collapse and Capture a Repeating Pattern in a Single Regex Expression并对其进行了调整以适应这一目的。
因此,希望防弹 RegEx用于从 * 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()和单一RegEx的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
答案 1 :(得分: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
UPDATE:由于你使用的是一些“不寻常”但有效的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
)
答案 2 :(得分:0)
如果使用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)
}
答案 3 :(得分:0)
这是通用解决方案:
((select|,)\s+((((`)?\w+\6?\.)?(`)?\w+\7?)(\s+as\s+(`)?\w+\9?)?\s*))+from\s
组$ 3包含具有可能的表和别名装饰的列。
您可以对表达的某些部分使用非捕获组 - (?...)。在这种情况下,改变反向引用号。
使用单行运行它并忽略大小写标志。也许您应该将标识符规范\ w +更改为更具体的[a-zA-Z] \ w *,例如。
答案 4 :(得分:0)
这是一个老问题,但我遇到了同样的问题,使用@CSᵠ正则表达式无法解决。 我创建了另一个正则表达式来在更广泛的列上执行任务,包括计算字段
preg_match_all('/(?<=^SELECT |, |\) )([a-z]+\.)?([a-z]+ )?(as )?([a-z]+)(?= ?,|$)/im');
如果将在复杂查询中使用,我建议使用我创建的完整函数:https://gist.github.com/pedrosancao/2498ed85b3c1834c5bdd
答案 5 :(得分:0)
我迟到了,但其中大部分对我来说太复杂了,而且是 PHP 特定的,我想这就是你想要的,但很多人可能不是这样。 我使用的 PCRE 风味的正则表达式是:
([\w`]+)(?=\s*,|\s+from\s+)
这匹配字母数字和反引号 ([\w`]+) 字符,后跟逗号(或空格和逗号)或空格和关键字“from”。
后者是使用正向前瞻实现的
(?=<expression>)
在这种情况下
(?=\s*,|\s+from\s+)
它检查前面的标记 [\w`]+ 后面是否有
\s*, # whitespace (0 or more) and comma
或 (|)
\s+from\s+ # the keyword 'from', surrounded by >=1 whitespace characters
然后你可以在任何语言风格中使用 case 标志来忽略大小写。
编辑:还应该注意这匹配别名而不是原始列名。