【问题标题】:How can I extract field names from SQL with Perl?如何使用 Perl 从 SQL 中提取字段名称?
【发布时间】:2010-02-04 02:50:44
【问题描述】:

我在一个文本文件中有一系列选择语句,我需要从每个选择查询中提取字段名称。如果某些字段不使用 to_char() 等嵌套函数,这将很容易。

给定可能有多个嵌套括号的选择语句字段,例如:

ltrim(rtrim(to_char(base_field_name, format))) renamed_field_name,

或者只是将base_field_name 作为一个字段的简单情况,Perl 中的正则表达式会是什么样子?

【问题讨论】:

  • 就在 9 分钟前,您发布了以下内容:stackoverflow.com/questions/2174015/… ...您不能决定使用哪种语言吗?
  • 另外,您忘记更改问题的正文。它仍然是 Python。
  • 出于显而易见的原因,如果用一种语言编写代码需要付出更多的努力,那么针对这种特定情况切换到另一种语言是有意义的。
  • -1 仅将 Python 更改为 Perl 并不能使您的(难以阅读和未格式化的)代码示例有效。我很确定这些在 Python 中甚至都无效。
  • @TheObserver - 你从 Alex Martelli 那里得到的答案不是“Python 正则表达式不适合嵌套结构”,而是“正则表达式不适合嵌套结构”。是什么让你认为用不同的语言询问同一个工具会得到不同的答案?它可以在 Perl 中完成,但它可以在 Python 或 Ruby 或 PHP 或 C# 中完成。尝试这样做只是一个可怕的想法,并且会引起巨大的头痛。如果您需要一个复杂的解析器,请编写一个解析器。不要一直问“我怎么敲这个螺丝?”

标签: sql regex perl


【解决方案1】:

不要尝试编写正则表达式解析器(尽管 perl 正则表达式可以处理这样的嵌套模式),请使用 SQL::Statement::Structure

【讨论】:

  • +1 表示 SQL::Statement 和朋友。虽然它不能处理每一个极端的扭曲 SQL,但它至少是适合这项工作的工具。
【解决方案2】:

为什么不询问目标数据库本身将如何解释查询?

在 perl 中,可以使用DBI 来查询 SQL 查询的准备表示。有时这是特定于数据库的:一些驱动程序(在 perl DBD:: 命名空间下)支持他们的 RDBMS 以类似于 RDBMS 的本机 C 或 C++ API 的方式描述语句的想法。

不过,一般情况下可以这样做,因为 DBI 会将结果列的名称放在语句句柄属性 NAME 中。例如,以下内容很有可能适用于任何支持 DBI 的 RDBMS:

use strict;
use warnings;
use DBI;

use constant DSN => 'dbi:YouHaveNotToldUs:dbname=we_do_not_know';

my $dbh = DBI->connect(DSN, ..., { RaiseError => 1 });

my $sth;
while (<>) {
  next unless /^SELECT/i;   # SELECTs only, assume whole query on one line
  chomp;
  my $sql = /\bWHERE\b/i ? "$_ AND 1=0" : "$_ WHERE 1=0"; # XXX ugly!
  eval {
    $sth = $dbh->prepare($sql);  # some drivers don't know column names
    $sth->execute();             # until after a successful execute()
  };
  print $@, next if $@;  # oops, problem with that one
  print join(', ', @{$sth->{NAME}}), "\n";
}

XXX 丑陋! 位试图在 SELECT 上附加一个始终为假的条件,以便当您 execute() 时 SQL 引擎不必做任何实际工作。这是一种非常幼稚的方法——/\bWHERE\b/i 测试不能比简单的正则表达式正确地解析出 SELECT 字段名称更正确地识别 SQL WHERE 子句——但它可能会起作用。

【讨论】:

  • 为什么不呢?因为世界没有理想,生活也没有。我只能访问数据输出(无标题)和用于生成输出的 SQL 查询。
【解决方案3】:

在我用过的办公室里有一个相关的问题:

my @SqlKeyWordList = qw/select from where .../; # (1)

my @Candidates =split(/\s/,$SqlSelectQuery);      # (2)

my %FieldHash;                                  # (3)
for my $Word (@Candidates)  { 
   next if grep($word,@SqlKeyWordList);
   $FieldHash($Word)++;
} 

评论:

  1. SqlKeyWordList 包含所有可能在 SQL 语句中的 SQL 关键字(我们使用 MySQL,有许多 SQL 方言,选择/构建这个列表是可行的,看看下面我的 cmets!)。如果有人决定使用关键字作为字段名称,那么您毕竟需要一个正则表达式(最好重构代码)。
  2. 将 SQL 语句拆分为单词列表,这是最棘手的部分,需要 tweeking。现在它使用 Perl 的“空格”(=not in word)概念来分割。
    这里可能建议拆分字段列表(选择 a、b、c)和 SQL 的“来自”部分,具体取决于您的 SQL 语句。
  3. %MyFieldHash 将在每个选择字段中包含一个条目(并且很糟糕,直到您验证了 SqlKeyWorkList 和 (2) 中的正则表达式)

小心

  • 此代码中没有任何内容无法在 Python 中完成。
  • 如果您可以影响上述 SQL 语句的创建,您的生活会轻松很多。 (例如,确保每个字段都写入评论)
  • 在这种解析方法中可能/将会出错的地方太多了,您确实应该通过更改流程来完全回避这个问题(从长远来看可以节省时间)。
  • 这是我们在办公室使用的正则表达式
我的@Candidates=split(/[\s \( \) \+ \, \* \/ \- \n \ \= \r ]+/,$SqlSelectQuery );

【讨论】:

  • 聪明,如果脆弱的话。 select 42 'let x=x' 以两种方式打破它。
  • @ysth: (s) 写这个的人,应该得到他得到的(以及一些)
【解决方案4】:

如何将每一行拆分为术语(用换行符替换每个括号、逗号和空格),然后排序:

perl -ne's/[(), ]/\n/g; print' < textfile | sort -u

您最终会得到很多内容,例如:

字段名1 字段名1 格式字符串 特里姆 rtrim t_char

【讨论】:

    猜你喜欢
    • 2021-01-03
    • 1970-01-01
    • 1970-01-01
    • 2012-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多