我正在尝试解析存储在日志数据库中的一些SQL查询 - 我不想将它们提交到SQL数据库,只是为了提取SELECT和WHERE子句中使用的字段。
我一直在摆弄Java,Python和Perl中的几个SQL解析器。对我的问题似乎更好的一个是SQL :: Parser和SQL :: Statement。有了这些,我能够编写以下代码:
#!/usr/bin/perl
use strict;
use SQL::Parser;
use SQL::Statement;
use Data::Dumper;
my $sql = "SELECT sl.plate,sp.fehadop FROM sppLines AS sl ".
"JOIN sppParams AS sp ON sl.specobjid = sp.specobjid ".
"WHERE fehadop < -3.5 ";
my $parser = SQL::Parser->new();
my $stmt = SQL::Statement->new($sql,$parser);
printf("COMMAND [%s]\n",$stmt->command);
printf("COLUMNS \n");
my @columns = @{$stmt->column_defs()};
foreach my $column ( @columns)
{
print " ".$column->{value}."\n";
}
printf("TABLES \n");
my @tables = $stmt->tables();
foreach my $table ( @tables)
{
print " ".$table->{name}."\n";
}
printf("WHERE COLUMNS\n");
my $where_hash = $stmt->where_hash();
print Dumper($where_hash);
对不起,如果时间太长,这是我能设计的最小,最独立的例子。
此代码的输出为:
COMMAND [SELECT]
COLUMNS
spplines.plate
sppparams.fehadop
TABLES
spplines
sppparams
WHERE COLUMNS
$VAR1 = {
'arg1' => {
'value' => 'fehadop',
'type' => 'column',
'fullorg' => 'fehadop'
},
'op' => '<',
'nots' => {},
'arg2' => {
'str' => '-?0?',
'fullorg' => '-3.5',
'name' => 'numeric_exp',
'value' => [
{
'fullorg' => '3.5',
'value' => '3.5',
'type' => 'number'
}
],
'type' => 'function'
},
'neg' => 0
};
解析器返回已使用真实表名称重命名的列名称(通过调用$stmt->column_defs()
获得)(例如 spplines .plate而不是 s1 .plate) - 这就是我想要的。
我还想要WHERE
子句中使用的列的名称。
我已经知道如何以递归方式解析$stmt->where_hash()
的结果(不包括使帖子清晰的代码),但即使从转储其内容,我也可以看到列名与表没有关联。
我想确保WHERE
子句中的列名称前面还有表名。解析$stmt->where_hash()
的结果后,我会得到sppparams.fehadop而不是fehadop。
这可能与SQL :: Parser一起使用吗?
由于 (大编辑 - 试图让问题更清晰)
答案 0 :(得分:1)
由于SQL :: Statement有eval_where
,我怀疑可能有更好的方法,但你可以尝试这样的函数:
get_column($stmt->column_defs(), $where_hash->{arg1});
sub get_column {
my ($columns, $arg) = @_;
return $arg->{fullorg} if ($arg->{type} ne 'column');
foreach my $col (@$columns) {
return $col->{value} if ($col->{fullorg} eq $arg->{fullorg});
my ($name) = ( $col->{fullorg} =~ /([^.]+)$/);
return $col->{value} if ($name eq $arg->{fullorg});
}
return $arg->{fullorg};
}