Perl 什么';使用这种替代机制进行DBI查询有什么问题?

Perl 什么';使用这种替代机制进行DBI查询有什么问题?,perl,bind,dbi,weak-typing,Perl,Bind,Dbi,Weak Typing,在中,这是建议多次执行查询的代码: $sth = $dbh->prepare_cached($statement); $sth->execute(@bind); $data = $sth->fetchall_arrayref(@attrs); $sth->finish; 但是,我看到许多*查询方法允许传递一个准备好的缓存语句句柄来代替查询字符串,这使得这成为可能: $sth = $dbh->prepare_cached($statement); $data = $

在中,这是建议多次执行查询的代码:

$sth = $dbh->prepare_cached($statement);
$sth->execute(@bind);
$data = $sth->fetchall_arrayref(@attrs);
$sth->finish;
但是,我看到许多*查询方法允许传递一个准备好的缓存语句句柄来代替查询字符串,这使得这成为可能:

$sth = $dbh->prepare_cached($statement);
$data = $dbh->selectall_arrayref($sth, \%attrs, @bind);
这种方法有什么问题吗?我还没见过它在野外使用

FWIW,我已经对这两个实现进行了基准测试。当在第一个实现中使用
fetchall\u arrayref
查询两个连续行时,第二种方法的速度略快(4%),而在第二个实现中使用
selectall\u arrayref

*支持此功能的查询方法的完整列表包括:

  • selectrow\u arrayref-具有准备语句的正常方法是fetchrow\u arrayref
  • 选择行\u hashref-“”获取行\u hashref
  • 选择All\u arrayref-“”获取全部\u arrayref
  • 选择所有\u hashref-“”获取所有\u hashref
  • 选择col_arrayref(实际上不算,因为它没有使用上述第一个代码路径的并行方法-因此 将准备好的语句与此方法一起使用就是使用第二个代码 (上面的路径)

不,这种方法没有错。不过,您的基准或其分析有问题

你已经声称了

$sth->execute(@bind);
$data = $sth->fetchall_arrayref(@attrs);
$sth->finish;
比打电话给

sub selectall_arrayref {
    my ($dbh, $stmt, $attr, @bind) = @_;
    my $sth = (ref $stmt) ? $stmt : $dbh->prepare($stmt, $attr)
        or return;
    $sth->execute(@bind) || return;
    my $slice = $attr->{Slice}; # typically undef, else hash or array ref
    if (!$slice and $slice=$attr->{Columns}) {
        if (ref $slice eq 'ARRAY') { # map col idx to perl array idx
            $slice = [ @{$attr->{Columns}} ];   # take a copy
            for (@$slice) { $_-- }
        }
    }
    my $rows = $sth->fetchall_arrayref($slice, my $MaxRows = $attr->{MaxRows});
    $sth->finish if defined $MaxRows;
    return $rows;
}
也许如果你摆脱了对
finish
的无用调用,你会发现第一个更快?请注意,差异小于5%的基准不是很有说服力;准确度没有那么高


更新:s/快于/慢于

只要您计划只执行一次提取,它就没有问题。当您使用
select*.*
方法时,所有数据都会在一个块中返回。我的DBI代码通常如下所示:

$sth = $dbh->prepare_cached($statement);
$sth->execute(@bind);
while (my $row = $sth->fetch) { # alias for fetchrow_arrayref
  # do something with @$row here
}
使用
select*.
方法没有与此等效的方法


如果您要调用
fetchall.*
(或者您只获取一行),那么继续使用带有语句句柄的
select*.*.*
方法。

我认为使用一个方法比另一个方法没有任何好处,除了第一个使用三行,第二个使用一行(第二种方法出现错误的可能性较小)。第一种方法可能更常用,因为“SELECT语句的典型方法调用序列是prepare、execute、fetch、fetch、…execute、fetch、fetch、…”,并给出了以下示例:

$sth = $dbh->prepare("SELECT foo, bar FROM table WHERE baz=?");

$sth->execute( $baz );

while ( @row = $sth->fetchrow_array ) {
  print "@row\n";
}

现在,我并不是建议程序员实际阅读文档(但愿如此!)但是,在一个旨在向您展示如何使用该模块的章节中,由于其在文档顶部的突出位置,我怀疑模块的作者更倾向于使用更详细的方法。至于原因,您的猜测和我的一样好。

性能差异不应是selectall_arrayref()之间的差异和fetchall_arrayref(),但在fetchall_arrayref()和自己在循环中执行fetch()之间,fetchall_arrayref()可能会更快

讨论性能

   If $max_rows is defined and greater than or equal to zero then it is
   used to limit the number of rows fetched before returning.
   fetchall_arrayref() can then be called again to fetch more rows.  This
   is especially useful when you need the better performance of
   fetchall_arrayref() but don't have enough memory to fetch and return
   all the rows in one go.

   Here's an example (assumes RaiseError is enabled):

     my $rows = []; # cache for batches of rows
     while( my $row = ( shift(@$rows) || # get row from cache, or reload cache:
                        shift(@{$rows=$sth->fetchall_arrayref(undef,10_000)||[]}) )
     ) {
       ...
     }

   That might be the fastest way to fetch and process lots of rows using
   the DBI, but it depends on the relative cost of method calls vs memory
   allocation.

   A standard "while" loop with column binding is often faster because the
   cost of allocating memory for the batch of rows is greater than the
   saving by reducing method calls. It's possible that the DBI may provide
   a way to reuse the memory of a previous batch in future, which would
   then shift the balance back towards fetchall_arrayref().

所以这是一个明确的“可能”。-)

事实上,他声称的是相反的。
selectall\u arrayref
execute/fetchall\u arrayref/finish
代码快4%。但是,是的,在~5%的准确率水平背后的点仍然成立。不,我(或者更确切地说,我的基准测试结果)声称事实正好相反——selectall_arrayref调用(第二种方法)比fetchall_arrayref调用(第一种方法)稍微快一些。@CanSpice,@Ether,Ops,我只是写了否定(从我的观点来看,这应该是显而易见的)修复了它。FWIW,我看不到从删除对
->finish
的调用中有任何变化(在500k次执行中)。此外,一些查询方法(例如selectrow\u arrayref)真的想要它,即使只有一个结果行。@CanSpice:PS。我刚刚通过via注意到你也是本地人。在meetup:DYes见,我总是一次获取所有数据,并尽可能优化查询,只返回我关心的数据片段。