如何在Perl中提高多个数据库查询的效率?

时间:2009-05-13 09:21:43

标签: database perl

我有一个驻留在多个方法中的查询,每个方法(查询)可以包含多个参数。我正在尝试减少文件大小和行数,使其更易于维护。以下是这样的事件:

$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 个答案:

答案 0 :(得分:6)

如果行数和可维护代码是您唯一的目标,那么您最好的选择是使用几个可用的精细ORM框架/库中的任何一个。 Class::DBIDBIx::Class是两个很好的起点。为了以防万一,你担心花费额外的时间来学习这些模块 - 不要:我只花了一个下午才开始并提高工作效率。以Class :: DBI为例,您的示例只是一行:

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

这些框架的唯一缺点是,非常复杂的SQL语句需要编写自定义方法学习,这可能需要一些额外的时间(不要太多)来解决。

答案 1 :(得分:4)

在每次数据库调用后检查错误都没有意义。多么乏味!

相反,当您连接到数据库时,将RaiseError选项设置为true。然后,如果发生数据库错误,将抛出异常。如果你没有捕获它(在eval {}块中),你的程序将会消息,类似于你上面手动执行的操作。

答案 2 :(得分:2)

“执行”函数 接受包含所有参数的数组。

您只需要找到一种方法来指示您要执行哪个语句句柄,并且您已完成...

将语句句柄保留在某个地方会好得多,因为如果你每次创建一个新句子并准备它,那么每次你没有真正撕掉“准备”的好处......

关于返回所有可以执行此操作的行(类似于“while fetchrow_hashref push”),请注意大量的结果集,这些结果集会让你的所有记忆都消耗殆尽!

答案 3 :(得分:2)

这是一个简单的方法,使用按关键字名称存储在哈希中的闭包/匿名子句(编译,但未经过其他测试),编辑后使用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");

# ...

答案 4 :(得分:1)

我对bubaker使用闭包的例子印象深刻。

同样,如果最初的目标是使代码库更小,更易于维护,我不禁想到,在任何人开始转换之前,有很多噪音要求从原始代码中删除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);