Что не так с этим альтернативным механизмом для выполнения запросов DBI? - PullRequest
5 голосов
/ 05 апреля 2011

В документации 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, я сравнил эти две реализации.И второй подход выглядит незначительно (на 4%) быстрее, при запросе двух последовательных строк с использованием fetchall_arrayref в первой реализации против selectall_arrayref во второй.

* Полный список методов запросакоторые поддерживают это:

  • selectrow_arrayref - обычный метод с подготовленными инструкциями - fetchrow_arrayref
  • selectrow_hashref - "" fetchrow_hashref
  • selectall_arrayref - "" fetchall_arrayref
  • selectcol_arrayref (на самом деле не считается, так как не имеет параллельного метода, использующего первый путь кода, как описано выше - поэтому единственный способ использовать подготовленные операторы с этим методом - это использоватьвторой путь кода выше)

Ответы [ 4 ]

6 голосов
/ 05 апреля 2011

В этом нет ничего плохого, если вы планируете сделать только один выбор. Когда вы используете методы 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*_* с дескриптором оператора.

4 голосов
/ 05 апреля 2011

Нет, в этом нет ничего плохого. Что-то не так с вашим тестом или его анализом.

Вы утверждали, что

$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% не очень показательны; точность не такая высокая.

Обновление : с / быстрее чем / медленнее /

2 голосов
/ 05 апреля 2011

Разница в производительности должна быть не между 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().

Так что это окончательное "возможно". : -)

0 голосов
/ 05 апреля 2011

Я не думаю, что действительно есть какое-то преимущество в использовании одной над другой, кроме того, что первая использует три строки, а вторая использует одну (меньше возможностей для ошибок со вторым методом).Первый может использоваться чаще, потому что документация гласит , что "типичная последовательность вызова метода для инструкции SELECT - подготовка, выполнение, выборка, выборка, ... выполнение, выборка, выборка, ..."и приводит этот пример:

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

$sth->execute( $baz );

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

Теперь я не предполагаю, что программисты действительно читают документацию (не дай бог!), но учитывая ее выдающееся положение в верхней части документации в разделе, предназначенном, чтобы показать вам, какчтобы использовать модуль, я подозреваю, что более подробный метод более предпочтителен автором модуля.Что касается того, почему, ваше предположение так же хорошо, как мое.

...