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

发布于 2024-10-29 08:25:35 字数 1013 浏览 0 评论 0原文

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 与在第二个实现中使用 selectall_arrayref 查询两个连续行时,第二种方法显得速度稍快 (4%)。

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

  • selectrow_arrayref - 带有准备好的语句的普通方法是 fetchrow_arrayref
  • selectrow_hashref - “” fetchrow_hashref
  • selectall_arrayref - “” fetchall_arrayref
  • selectall_hashref - “” fetchall_hashref
  • selectcol_arrayref (实际上不算数,因为它没有使用如上所述的第一个代码路径的并行方法 - 所以唯一的方法 要在此方法中使用准备好的语句,请使用第二个代码 上面的路径)

In the DBI documentation, this is the recommended code for executing a query many times:

$sth = $dbh->prepare_cached($statement);
$sth->execute(@bind);
$data = $sth->fetchall_arrayref(@attrs);
$sth->finish;

However, I see that many* query methods allow passing a prepared and cached statement handle in place of a query string, which makes this possible:

$sth = $dbh->prepare_cached($statement);
$data = $dbh->selectall_arrayref($sth, \%attrs, @bind);

Is there anything wrong with this approach? I haven't seen it used in the wild.

FWIW, I have benchmarked these two implementations. And the second approach appears marginally (4%) faster, when querying for two consecutive rows using fetchall_arrayref in the first implementation vs selectall_arrayref in the second.

* The full list of query methods which support this are:

  • selectrow_arrayref - normal method with prepared statements is fetchrow_arrayref
  • selectrow_hashref - " " fetchrow_hashref
  • selectall_arrayref - " " fetchall_arrayref
  • selectall_hashref - " " fetchall_hashref
  • selectcol_arrayref (doesn't really count, as it has no parallel method using the first code path as described above - so the only way
    to use prepared statements with this method is to use the second code
    path above)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

终难遇 2024-11-05 08:25:35

只要您计划只进行一次提取,就没有任何问题。当您使用 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*_* 方法。

There's nothing wrong with it, as long as you were planning to do only one fetch. When you use the select*_* methods, all the data comes back in one chunk. My DBI code more often looks like this:

$sth = $dbh->prepare_cached($statement);
$sth->execute(@bind);
while (my $row = $sth->fetch) { # alias for fetchrow_arrayref
  # do something with @$row here
}

There's no equivalent to this using a select*_* method.

If you're going to call fetchall_* (or you're only fetching 1 row), then go ahead and use a select*_* method with a statement handle.

梦境 2024-11-05 08:25:35

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

您声称这

$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;
}

Maybe 慢,如果您摆脱了对 finish 无用的调用,您会发现第一个更快吗?请注意,差异小于 5% 的基准并不能说明问题;准确率没那么高。

更新:s/快于/慢于/

No, there's nothing wrong that approach. There is something wrong with your benchmark or its analysis, though.

You've claimed that

$sth->execute(@bind);
$data = $sth->fetchall_arrayref(@attrs);
$sth->finish;

is slower than a call to

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;
}

Maybe if you got rid of the useless call to finish you'll find the first faster? Note that benchmarks with less than 5% difference are not very telling; the accuracy isn't that high.

Update: s/faster than/slower than/

孤独岁月 2024-11-05 08:25:35

性能差异不应该出现在 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().

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

The performance difference should not be between selectall_arrayref() and fetchall_arrayref() but between fetchall_arrayref() and doing a fetch() in a loop yourself. fetchall_arrayref() may be faster as it is hand optimized in C.

The docs for fetchall_arrayref discuss performance...

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

So that's a definitive "maybe". :-)

通知家属抬走 2024-11-05 08:25:35

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

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

$sth->execute( $baz );

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

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

I don't think there's really any advantage to using one over the other, other than that the first uses three lines and the second uses one (less possibility for bugs with the second method). The first might be more commonly used because the documentation states that the "typical method call sequence for a SELECT statement is prepare, execute, fetch, fetch, ... execute, fetch, fetch, ..." and gives this example:

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

$sth->execute( $baz );

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

Now, I'm not suggesting that programmers actually read the documentation (heaven forbid!) but given its prominence near the top of the documentation in a section designed to show you how to use the module, I would suspect that the more-verbose method is slightly more preferred by the module's author. As to why, your guess is as good as mine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文