Как сделать несколько запросов к базе данных более эффективным в Perl? - PullRequest
5 голосов
/ 13 мая 2009

У меня есть запросы, которые находятся в нескольких методах, каждый из которых (запрос) может содержать несколько параметров. Я пытаюсь уменьшить размер файла и количество строк, чтобы сделать его более понятным. Ниже приведен такой случай:

$sql_update = qq { UPDATE database.table
                    SET column = 'UPDATE!'
                   WHERE id = ?
                };

$sth_update = $dbh->prepare($sql_update);

if ($dbh->err) {
    my $error = "Could not prepare statement. Error: ". $dbh->errstr ." Exiting at line " . __LINE__;
    print "$error\n";
    die;
}

$sth_rnupdate->execute($parameter);

if ($dbh->err) {
    my $error = "Could not execute statement. Error: ". $dbh->errstr ." Exiting at line " . __LINE__;
    print "$error\n";
    die;
}

Это всего лишь один пример, однако, есть несколько других примеров выбора, которые содержат только один параметр, который нужно передать, однако есть и другие с двумя или более параметрами. Я думаю, мне просто интересно, можно ли было бы инкапсулировать все это в функцию / метод, передать массив параметров, как параметры будут заполнены в функции execute ()?

Если бы это было возможно, я мог бы написать метод, который просто передал бы SQL-запрос и параметры и вернул ссылку на извлеченные записи. Это звучит безопасно на всех?

Ответы [ 5 ]

6 голосов
/ 13 мая 2009

Если вашей единственной целью является подсчет строк и обслуживаемый код, лучшим вариантом будет использование любой из нескольких доступных сред / библиотек ORM. Class :: DBI и DBIx :: Class - две прекрасные отправные точки. На всякий случай, вы беспокоитесь о том, чтобы потратить дополнительное время на изучение этих модулей - не надо: мне понадобился всего один день, чтобы начать и продуктивно работать. Например, используя Class :: DBI, ваш пример состоит из одной строки:

Table->retrieve(id => $parameter)->column('UPDATE!')->update;

Единственный недостаток (если таковой) этих платформ заключается в том, что очень сложные операторы SQL требуют написания пользовательских методов, что может занять некоторое дополнительное время (не слишком много), чтобы обойти.

4 голосов
/ 15 мая 2009

Нет смысла проверять ошибки после каждого отдельного вызова базы данных. Как скучно!

Вместо этого при подключении к базе данных установите для параметра RaiseError значение true. Затем, если произойдет ошибка базы данных, будет сгенерировано исключение. Если вы не поймаете его (в блоке eval {}), ваша программа умрет с сообщением, аналогичным тому, что вы делали вручную выше.

2 голосов
/ 13 мая 2009

Вот простой подход, использующий замыкания / анонимные подпрограммы, сохраненные в хэше по имени ключевого слова (компилируется, но не тестируется иначе), отредактированный с использованием RaiseError:

# define cached SQL in hash, to access by keyword
#
sub genCachedSQL {
  my $dbh = shift;
  my $sqls = shift;  # hashref for keyword => sql query
  my %SQL_CACHE;
  while (my($name,$sql) = each %$sqls) {
     my $sth = $dbh->prepare($sql);
     $SQL_CACHE{$name}->{sth} = $sth;

     $SQL_CACHE{$name}->{exec} = sub {  # closure for execute(s)
        my @parameters = @_;
        $SQL_CACHE{$name}->{sth}->execute(@parameters);

        return sub {  # closure for resultset iterator  - check for undef
           my $row; eval { $row = $SQL_CACHE{$name}->{sth}->fetchrow_arrayref(); };
           return $row;
        }  # end resultset closure

     }  # end exec closure

  }  # end while each %$sqls

  return \%SQL_CACHE;

}  # end genCachedSQL


my $dbh = DBI->connect('dbi:...', { RaiseError => 1 });

# initialize cached SQL statements
#
my $sqlrun = genCachedSQL($dbh,
 {'insert_table1' => qq{ INSERT INTO database.table1 (id, column) VALUES (?,?) },
  'update_table1' => qq{ UPDATE database.table1 SET column = 'UPDATE!' WHERE id = ? },
  'select_table1' => qq{ SELECT column FROM database.table1 WHERE id = ? }});

# use cached SQL
#
my $colid1 = 1;
$sqlrun->{'insert_table1'}->{exec}->($colid1,"ORIGINAL");
$sqlrun->{'update_table1'}->{exec}->($colid1);
my $result = $sqlrun->{'select_table1'}->{exec}->($colid1);
print join("\t", @$_),"\n" while(&$result());

my $colid2 = 2;
$sqlrun->{'insert_table1'}->{exec}->($colid2,"ORIGINAL");

# ...
2 голосов
/ 13 мая 2009

Функция «execute» принимает массив, содержащий все ваши параметры.

Вам просто нужно найти способ указать, какой дескриптор оператора вы хотите выполнить, и все готово ...

Было бы гораздо лучше где-то хранить свои операторы, потому что, если вы каждый раз создаете новый и готовите его каждый раз, вы на самом деле не теряете преимущества "подготовки" ...

О возврате всех строк вы можете сделать это (что-то вроде "while fetchrow_hashref push"), остерегайтесь больших наборов результатов, которые могут съесть всю вашу память!

1 голос
/ 14 мая 2009

Я очень впечатлен примером использования бубакером укупорочного средства для этого.

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

Если $dbh был создан с атрибутом RaiseError, установленным в атрибутах, большая часть этого кода исчезнет:

$sql_update = qq { UPDATE database.table
                   SET column = 'UPDATE!'
                   WHERE id = ?
              };
$sth_update = $dbh->prepare($sql_update);
$sth_update->execute($parameter);

Я не вижу, чтобы обработка ошибок в исходном коде добавляла многое, чего вы не получите от ванили die, производимой RaiseError, но, если это важно, взгляните на HandleError атрибут в справочной странице DBI.

Кроме того, если такие операторы не используются повторно (что часто является главной целью их подготовки, чтобы кэшировать, как они оптимизируются; другая причина состоит в том, чтобы смягчить против внедрения SQL с использованием заполнителей), то почему бы не использовать do

$dbh->do($sql_update, \%attrs, @parameters);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...