Perl SQL :: Parser表别名替换:适用于SELECT列名,但不适用于WHERE列名

时间:2016-03-08 03:17:12

标签: sql perl parsing

我正在尝试解析存储在日志数据库中的一些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一起使用吗?

由于 (大编辑 - 试图让问题更清晰)

1 个答案:

答案 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};
}