【问题标题】:Fetch all records using Perl DBI使用 Perl DBI 获取所有记录
【发布时间】:2012-05-02 10:16:09
【问题描述】:

我有一个使用DBI 连接的Perl 脚本。我使用子例程打开并读取 SQL 脚本文件。我只打印一条记录,我应该再打印两条(总共三条记录)。如何获取所有记录?

结果:

Alert:OUTBOUND_DATA:0

脚本:

my $dbh_oracle = DBI->connect(
          $CFG{oracle_dbi_connect},
          $CFG{db_user},
          $CFG{db_cred},
          {AutoCommit => 0,
           RaiseError => 0,
           PrintError => 0}) or die ("Cannot connect to the database: ".$DBI::errstr."\n");

my ($val1, $val2) = get_data();
print "Alert:$val1:$val2\n";

send_email("Alert:$val1:$val2");

sub get_data
{
  undef $/;
  open (my $QFH, "< /sql/summary.sql") or die "error can't open this file $!";
  my $sth= $dbh_oracle->prepare(<$QFH>) or
      die ("Cannot connect to the database: ".$DBI::errstr."\n");
  $sth->execute;
  close $QFH;
  my $row = $sth->fetchrow_hashref;
  $sth->finish;
  return @$row{'MYTABLE','FLAG'};
}

sub send_email {
    my $message = shift;
    open (MAIL, "|/usr/sbin/sendmail -t") or die "Can't open sendmail: $!";
    print MAIL "To: me\@test.com\n";
    print MAIL "From: Data\n";
    print MAIL "\n";
    print MAIL $message;
    close MAIL;
}
exit;

运行查询的结果:(超过 1 个记录)

MYTABLE                  FLAG
----------------------- ----------
OUTBOUND_DATA         0
MSGS_BY_DIM                  0
INBOUND_DATA         0

3 rows selected.

【问题讨论】:

  • 您可以设置 RaiseError =&gt; 1 并消除对 die() 调用的需要。
  • 另外,我注意到您同时禁用了 RaiseErrorPrintError。第一个导致错误是致命的,第二个至少会打印它们(但不会杀死你的代码)。您可能应该至少将其中一个设置为真值,以免您的错误被默默丢弃。

标签: perl dbi


【解决方案1】:

您可以通过多种不同的方式从语句句柄中检索数据。最常见的很简单,它们的使用如下所示:

my @row_array = $sth->fetchrow_array;
my $array_ref = $sth->fetchrow_arrayref;
my $hash_ref  = $sth->fetchrow_hashref;

第一个,fetchrow_array,将依次返回每一行作为一个数组。使用从上述选择返回的数据的示例可能是:

while (my @row_array = $sth->fetchrow_array) {
    print $row_array[0], " is ", $row_array[1], " years old, and has a " , 
          $row_array[2], "\n";
}

第二个例子类似,但返回的是数组引用而不是数组:

while (my $array_ref = $sth->fetchrow_arrayref) {
    print $array_ref->[0], " is ", $array_ref->[1], 
          " years old, and has a " , $array_ref->[2], "\n";
}

第三个例子,fetchrow_hashref,通常是最易读的:

while (my $hash_ref = $sth->fetchrow_hashref) {
    print $hash_ref->{name}, " is ", $hash_ref->{age}, 
          " years old, and has a " , $hash_ref->{pet}, "\n";
}

【讨论】:

  • 关于 fetchrow_hashref 的注释,除非您更改 FetchHashKeyName 属性,否则键将大写。
  • 如果不提及@gpojd 对selectall_ 函数的回答,这个答案是不完整的
【解决方案2】:

这还取决于您如何构建整个脚本。您的 get_data() 调用只允许返回一对值。我至少看到了几个选项:要么返回一个包含所有数据的哈希(引用)并让main 组装它,要么使用前面提到的循环结构并在子例程中制作消息体,只返回一个标量字符串.

要将所有数据作为哈希引用返回,get_data 子例程可能如下所示(注意我使用的是fetchall_hashref 而不是fetchrow_hashref

sub get_data
{
  undef $/;
  open (my $QFH, "< /sql/summary.sql") or die "error can't open this file $!";
  my $sth= $dbh_oracle->prepare(<$QFH>) or
      die ("Cannot connect to the database: ".$DBI::errstr."\n");
  $sth->execute;
  close $QFH;
  my $hash_ref = $sth->fetchall_hashref('MYTABLE');
  $sth->finish;
  return $hash_ref;
}

您从main 调用它并使用如下输出:

my $hash_ref = get_data();
my $message = "";
foreach my $table (sort keys %$hash_ref) {
    $message .= join(":", "Alert", $table, $$hash_ref{$table}{'FLAG'}) . "\n";
}

这将导致$message 包含:

Alert:INBOUND_DATA:0
Alert:MSGS_BY_DIM:0
Alert:OUTBOUND_DATA:0

你可能要礼貌:

$dbh_oracle->disconnect;

在你退出之前。

这有一些问题,例如您已将 SQL 隐藏在外部脚本中,但我已采用硬编码键(MYTABLE,我假设它在您的查询中是唯一的)和值(FLAG ) 在脚本中,稍后您想对此进行扩展时会有所限制。

【讨论】:

  • 这就是我想要的。我试图在使用子程序的基础上进行构建。感谢您提供使用main 示例作为组合所有输出的方式。
【解决方案3】:

这一行应该是一个循环:

my $row = $sth->fetchrow_hashref;

应该是:

my @rows;
while ( my $row = $sth->fetchrow_hashref ) {
    push @rows, $row;
}
return @rows;

如果您希望 DBI 为您执行循环,请查看 selectall_arrayrefselectall_hashref

【讨论】:

  • 我应该把$sth-&gt;finish放在哪里?那是在我return @$row之后吗?
  • @cjd143SD,当你完成循环时,$sth->fetchrow_hashref 将返回 undef 并自行“完成”。
【解决方案4】:

fetchrow_ 方法实际上一次只获取一行。

如果您想要某些列的所有行,您可能会低效地推动数据结构,或者使用适合您情况的调用。

在我看来你想使用selectcol_arrayref

my $ary_ref = $dbh->selectcol_arrayref(
    "select id, name from table",
    { Columns=>[1,2] }
);

列索引指的是结果集中列的位置,而不是原始表。

您使用返回结果的方式也需要更改以处理所有返回的行。

另外,你有:

sub get_data
{
  undef $/;

因为你吞食了包含 SQL 的文件。但是,$/ 是一个全局变量。您应该在尽可能小的范围内使用@987654322@ $/。所以:

my $sql = do { local $/; <$fh> };

【讨论】:

  • 是的,我同意在这些情况下我想要查询中的所有内容,看来selectcol_arrayref 将是选择。您对使用 local $/ 的建议,我会将其作为子程序包含在内还是将其保留在外面?谢谢。
猜你喜欢
  • 1970-01-01
  • 2011-05-11
  • 1970-01-01
  • 2011-06-13
  • 2013-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-13
相关资源
最近更新 更多