这种替代机制进行DBI查询有什么问题?

时间:2011-04-04 21:07:21

标签: perl bind dbi weak-typing

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

$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和第二次执行中的selectall_arrayref查询连续两行时,显得略微(4%)。

  

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

     
      
  • selectrow_arrayref - 使用预准备语句的常规方法是fetchrow_arrayref
  •   
  • selectrow_hashref - “”fetchrow_hashref
  •   
  • selectall_arrayref - “”fetchall_arrayref
  •   
  • selectall_hashref - “”fetchall_hashref
  •   
  • selectcol_arrayref(实际上没有计算,因为它没有使用上面描述的第一个代码路径的并行方法 - 所以唯一的方法   使用这种方法准备语句就是使用第二个代码   上面的路径)
  •   

4 个答案:

答案 0 :(得分: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*_*方法。

答案 1 :(得分: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 /快于/慢于/

答案 2 :(得分:2)

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

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

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

答案 3 :(得分:0)

我认为使用一个优于另一个确实没有任何优势,除了第一个使用三行而第二个使用一个(使用第二个方法的错误的可能性更小)。第一个可能更常用,因为the documentation states“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";
}

现在,我并不是建议程序员真正阅读文档(天堂禁止!),但鉴于其突出位于文档顶部附近,旨在向您展示如何使用该模块,我怀疑它更多-verbose方法稍微优先于模块的作者。至于为什么,你的猜测和我的一样好。