Mysql 如何将WHERE子句逻辑与DBI一起重用?
免责声明:我第一次使用DBI 我有一个MySQL表,其中包含许多索引字段(f1、f2、f3等),这些字段用于通过长时间运行的进程生成WHERE子句,这些进程在执行各种清理和测试操作的数据库块上进行迭代 此代码的当前版本的工作原理如下:Mysql 如何将WHERE子句逻辑与DBI一起重用?,mysql,database,perl,iteration,where,Mysql,Database,Perl,Iteration,Where,免责声明:我第一次使用DBI 我有一个MySQL表,其中包含许多索引字段(f1、f2、f3等),这些字段用于通过长时间运行的进程生成WHERE子句,这些进程在执行各种清理和测试操作的数据库块上进行迭代 此代码的当前版本的工作原理如下: sub get_list_of_ids() { my ($value1, $value2, $value3...) = @_; my $stmt = 'SELECT * FROM files WHERE 1'; my @args;
sub get_list_of_ids() {
my ($value1, $value2, $value3...) = @_;
my $stmt = 'SELECT * FROM files WHERE 1';
my @args;
if (defined($value1)) {
$stmt .= ' AND f1 = ?';
push(@args, $value1);
}
# Repeat for all the different fields and values
my $select_sth = $dbh->prepare($stmt) or die $dbh->errstr;
$select_sth->execute(@args) or die $select_sth->errstr;
my @result;
while (my $array = $select_sth->fetch) {
push(@result, $$array[0]);
}
return \@result;
}
sub function_A() {
my ($value1, $value2, $value3...) = @_;
my $id_aref = get_list_of_ids($value1, $value2, $value3...);
foreach my $id (@$id_aref) {
# Do something with $id
# And something else with $id
}
}
sub function_B() {
my ($value1, $value2, $value3...) = @_;
my $id_aref = get_list_of_ids($value1, $value2, $value3...);
foreach my $id (@$id_aref) {
# Do something different with $id
# Maybe even delete the row
}
}
无论如何,我将要在数据库中转储更多的行,并且我很清楚上面的代码无法扩展。我可以想出几种基于其他语言的修复方法。在Perl中处理它的最佳方法是什么
需要注意的关键点是,\u id()的get\u list\u中的逻辑太长,无法在每个函数中复制;所选行上的操作非常不同
提前感谢。我想“放大”是指维护方面,而不是性能方面
代码的关键更改是将参数作为列/值对传递,而不是带有假定列集的值列表。这将允许代码处理可能添加的任何新列
DBI->selectcol\u arrayref
使用C编写,既方便又快速
如果在connect
调用中打开RaiseError
,DBI将在出现错误时抛出异常,而不必一直写入或死亡…
。你应该这样做
最后,由于我们是从可能不受信任的用户输入编写SQL,所以我注意了转义列名
其余部分在中进行了解释,您可以看到代码一步一步地被转换
sub get_ids {
my %search = @_;
my $sql = 'SELECT id FROM files';
if( keys %search ) {
$sql .= " WHERE ";
$sql .= join " AND ", map { "$_ = ?" }
map { $dbh->quote_identifier($_) }
keys %search;
}
return $dbh->selectcol_arrayref($sql, undef, values %search);
}
my $ids = get_ids( foo => 42, bar => 23 );
如果您希望get_id
返回一个庞大的列表,太多的内容无法保存在内存中,那么您可以返回语句句柄并进行迭代,而不是取出整个数组并将其存储在内存中
sub get_ids {
my %search = @_;
my $sql = 'SELECT id FROM files';
if( keys %search ) {
$sql .= " WHERE ";
$sql .= join " AND ", map { "$_ = ?" }
map { $dbh->quote_identifier($_) }
keys %search;
}
my $sth = $dbh->prepare($sql);
$sth->execute(values %search);
return $sth;
}
my $sth = get_ids( foo => 42, bar => 23 );
while( my $id = $sth->fetch ) {
...
}
您可以通过在数组上下文中返回ID列表或在标量中返回语句句柄来组合这两种方法
sub get_ids {
my %search = @_;
my $sql = 'SELECT id FROM files';
if( keys %search ) {
$sql .= " WHERE ";
$sql .= join " AND ", map { "$_ = ?" }
map { $dbh->quote_identifier($_) }
keys %search;
}
# Convenient for small lists.
if( wantarray ) {
my $ids = $dbh->selectcol_arrayref($sql, undef, values %search);
return @$ids;
}
# Efficient for large ones.
else {
my $sth = $dbh->prepare($sql);
$sth->execute(values %search);
return $sth;
}
}
my $sth = get_ids( foo => 42, bar => 23 );
while( my $id = $sth->fetch ) {
...
}
my @ids = get_ids( baz => 99 );
最终,您将希望停止手工编写SQL,并使用对象关系映射器(ORM),如。ORM的主要优点之一是它非常灵活,可以为您完成上述任务。类可以返回一个简单的结果列表,也可以返回非常强大的迭代器。迭代器是惰性的,在您开始提取行之前,它不会执行查询,从而允许您根据需要更改查询,而不必使提取例程复杂化
my $ids = get_ids( foo => 23, bar => 42 );
$ids->rows(20)->all; # equivalent to adding LIMIT 20
占位符在哪里映射到搜索哈希中的值?我看到了在映射管道中使用键的位置,但没有看到值。要使用一个值,您需要$search{$\u0},对吗?一定是在重构过程中丢失了它,我已经修复了它。这些值被传递到选择col\u arrayref()
中。通常情况下,您不能信任键
和值
的顺序,但对于相同的未修改散列,它们保证以相同的顺序返回。所谓“放大”,我的意思是将有数千万行,因此我预计将所有标识符作为标量存储在一个数组中需要比我可用的更多的RAM。但是,selectcol\u arrayref
可能会大大简化get\u ids()逻辑,这样我就可以在需要它的每个函数中重用它。我试试看。我也将签出DBIx::Class,谢谢。@如果您希望查询返回数百万行,我将扩展它以解决节省内存的问题。