我有一个驻留在多个方法中的查询,每个方法(查询)可以包含多个参数。我正在尝试减少文件大小和行数,使其更易于维护。以下是这样的事件:
$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查询和参数,然后获取对提取的记录的引用。这听起来很安全吗?
答案 0 :(得分:6)
如果行数和可维护代码是您唯一的目标,那么您最好的选择是使用几个可用的精细ORM框架/库中的任何一个。 Class::DBI和DBIx::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);