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、私はこれら 2 つの実装をベンチマークしました。fetchall_arrayrefまた、2 番目の方法は、最初の実装と 2 番目の実装を使用して 2 つの連続する行をクエリする場合、わずかに (4%) 高速に見えますselectall_arrayref

* これをサポートするクエリ メソッドの完全なリストは次のとおりです。

  • selectrow_arrayref - 準備済みステートメントを使用する通常の方法は fetchrow_arrayref です
  • selectrow_hashref - " " fetchrow_hashref
  • selectall_arrayref - " " fetchall_arrayref
  • selectall_hashref - " " fetchall_hashref
  • selectcol_arrayref (上記のように最初のコード パスを使用する並列メソッドがないため、実際にはカウントされません。したがって、このメソッドで準備済みステートメントを使用する唯一の方法は、上記の 2 番目のコード パスを使用することです)
4

4 に答える 4

6

フェッチを1回だけ実行することを計画している限り、問題はありません。メソッドを使用するselect*_*と、すべてのデータが1つのチャンクに戻されます。私の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

最初の方法では 3 行を使用し、2 番目の方法では 1 行を使用する (2 番目の方法ではバグの可能性が少ない) ことを除けば、どちらを使用しても特に利点はないと思います。ドキュメントには、「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 に答える