5

DBI 文档中,这是多次执行查询的推荐代码:

$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_arrayref当在第一个实现中查询两个连续的行时,第二种方法看起来稍微快一些(4%),而不是在selectall_arrayref第二个实现中。

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

  • selectrow_arrayref - 准备好的语句的正常方法是 fetchrow_arrayref
  • selectrow_hashref - " " fetchrow_hashref
  • selectall_arrayref - " " fetchall_arrayref
  • selectall_hashref - " " fetchall_hashref
  • selectcol_arrayref (实际上不算数,因为它没有使用上述第一个代码路径的并行方法 - 所以使用这种方法的准备好的语句的唯一方法是使用上面的第二个代码路径)
4

4 回答 4

6

只要您打算只进行一次取货,这没有什么问题。当您使用这些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_*(或者您只获取 1 行),请继续使用select*_*带有语句句柄的方法。

于 2011-04-04T21:54:27.117 回答
4

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

你声称

$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/快于/慢于/

于 2011-04-04T21:41:25.330 回答
2

性能差异不应该在 selectall_arrayref() 和 fetchall_arrayref() 之间,而是在 fetchall_arrayref() 和自己在循环中执行 fetch() 之间。fetchall_arrayref() 可能会更快,因为它是在 C 中手动优化的

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().

所以这是一个确定的“也许”。:-)

于 2011-04-05T05:15:11.567 回答
0

我认为使用一个比另一个没有任何优势,除了第一个使用三行,第二个使用一个(第二种方法的错误可能性较小)。第一个可能更常用,因为文档指出“SELECT 语句的典型方法调用顺序是准备、执行、获取、获取……执行、获取、获取……”并给出了这个例子:

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

$sth->execute( $baz );

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

现在,我并不是建议程序员实际阅读文档(天堂禁止!),但鉴于它在旨在向您展示如何使用模块的部分中靠近文档顶部的突出位置,我怀疑更详细的方法模块的作者更喜欢它。至于为什么,你的猜测和我的一样好。

于 2011-04-04T23:44:32.797 回答