我的要求是,我想将一些清单值映射到某些组。以下是我的代码:
@selectbox1 => contains the selected select groups
@selectbox2 => contains selected checklist
法典:
foreach $select1(@selectbox1) {
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box1 = $dbslave -> prepare($sql_select1);
$box1 -> execute();
while($select_box1= $box1->fetchrow_array())
{
push (@box1,$select_box1);
}
my $box_1 = @box1; # currently I tried like this to store the current value .NEED CORRECTION HERE
foreach $select2(@selectbox2) {
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box2 = $dbslave -> prepare($sql_select2);
$box2 -> execute();
while($select_box2 = $box2->fetchrow_array())
{
push (@box2,$select_box2);
}
my $box_2 = @box2; # currently I tried like this to store the current value .NEED CORRECTION HERE
my $sql_insert = "insert into checklist_group_mapping values ('',$box_2,$box_1)";
my $ins = $dbslave -> prepare($sql_insert);
$ins -> execute();
}
}
如何将数组的当前值分配给变量,以便将其插入到映射表中?
你需要阅读"上下文",特别是"标量上下文"和"数组上下文"。
当你写:
my $box_1 = @box1;
您正在提供标量上下文,在标量上下文中,@box1
返回数组中的元素数。 如果你写:
my($box_1) = @box1;
您将提供数组上下文,在数组上下文中,@box1
的第一个元素将分配给数组上下文的第一个元素,$box_1
— @box1
的其余元素将被删除。(这很可能是您所追求的;您可能正在尝试为@selectbox1
中每个名称选择单个 ID 值。
从您尝试在代码中使用 $box_1
和 $box_2
变量的方式来看,您希望获取一个包含来自@box1
的所有值的单个字符串和另一个包含来自@box2
的所有值的单个字符串,并且它们可能需要呈现给括在单引号中的 DBI 驱动程序。
您可以使用以下命令将空格分隔的值放入字符串中:
my $box_1 = "@box1";
如果需要逗号分隔的值,可以使用:
my $box_1;
{ local $" = ","; $box_1 = "@box_1"; }
$"
(又名use English '-no_match_vars';
下的$LIST_SEPARATOR
(必须本地化以防止损坏,但这意味着您必须将$box_1
的定义与分配分开(因为如果您不这样做,当您离开{...}
块时,$box_1
将被销毁(。
现在,为了保护它以便 SQL 可以工作,您需要使用 quote
方法:
$box1 = $dbslave->quote($box1);
或:
my $box1 = $dbslave->quote("@box1");
组合这些更改,我们得到:
#!/usr/bin/env perl
use strict;
use warnings;
### Improved, but not operational
# use DBI;
my @selectbox1 = ( "group1", "group2", "group3" );
my @selectbox2 = ( "check1", "check2", "check3" );
my $dbslave;
# $dbslave = DBI->connect(...) or die "A horrible death";
foreach my $select1 (@selectbox1)
{
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box1 = $dbslave->prepare($sql_select1);
$box1->execute();
my @box1;
while (my $select_box1 = $box1->fetchrow_array())
{
push @box1, $select_box1;
}
my $box_1 = $dbslave->quote("@box1");
foreach my $select2(@selectbox2)
{
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box2 = $dbslave->prepare($sql_select2);
$box2->execute();
my @box2;
while (my $select_box2 = $box2->fetchrow_array())
{
push @box2, $select_box2;
}
my $box_2 = $dbslave->quote("@box2");
my $sql_insert = "insert into checklist_group_mapping values ('', $box_2, $box_1)";
my $ins = $dbslave->prepare($sql_insert);
$ins->execute();
}
}
请注意,两个 SELECT 语句假定 select box 字符串不包含有趣的字符(具体来说,没有单引号(。 如果你负责@selectbox1
和@selectbox2
的内容,那没关系。 如果它们包含用户输入,则必须清理该输入,或再次使用$dbslave->quote()
,或使用占位符。 我将忽略这个问题。
您还将标量上下文与 $box1->fetchrow_array()
一起使用,这不会产生您想要的答案(尽管fetchrow_array()
上下文相关,但手册警告您要小心(。 我会使用类似的东西:
my @box1;
while (my @row = $box1->fetchrow_array())
{
push @box1, $row[0];
}
my $box_1 = $dbslave->quote("@box1");
您还需要使用函数。 您的代码中有一个明显的重复,可以封装到使用两次的单个函数中:
#!/usr/bin/perl
use strict;
use warnings;
# use DBI;
my @selectbox1 = ( "group1", "group2", "group3" );
my @selectbox2 = ( "check1", "check2", "check3" );
my $dbslave;
# $dbslave = DBI->connect(...) or die "A horrible death";
sub fetch_all
{
my($dbh, $sql) = @_;
my $sth = $dbh->prepare($sql);
$sth->execute();
my @results;
while (my @row = $sth->fetchrow_array())
{
push @results, $row[0];
}
my $result = $dbslave->quote("@results");
return $result;
}
foreach my $select1 (@selectbox1)
{
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box_1 = fetch_all($dbslave, $sql_select1);
foreach my $select2(@selectbox2)
{
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box_2 = fetch_all($dbslave, $sql_select2);
my $sql_insert = "insert into checklist_group_mapping values ('', $box_2, $box_1)";
my $ins = $dbslave->prepare($sql_insert);
$ins->execute();
}
}
INSERT 语句应转换为使用占位符,以便可以准备一次并使用多次:
my $sql_insert = "insert into checklist_group_mapping values ('', ?, ?)";
my $ins = $dbslave->prepare($sql_insert);
foreach my $select1 (@selectbox1)
{
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box_1 = fetch_all($dbslave, $sql_select1);
foreach my $select2(@selectbox2)
{
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box_2 = fetch_all($dbslave, $sql_select2);
$ins->execute($box_1, $box_2);
}
}
实际上,两个 SELECT 语句也应该参数化并准备一次并重用。 我没有表现出这种变化,因为(a(我很懒惰,(b(有一个更大的变化仍然更有效。
当我们查看您真正在做什么时,它应该都是一个 SQL 语句:
#!/usr/bin/perl
use strict;
use warnings;
# use DBI;
my @selectbox1 = ( "group1", "group2", "group3" );
my @selectbox2 = ( "check1", "check2", "check3" );
my $dbslave;
# $dbslave = DBI->connect(...) or die "A horrible death";
sub placeholder_list
{
my($n) = @_;
die "$n should be larger than 0" if $n <= 0;
my $list = "(?" . ",?" x ($n - 1) . ")";
return $list;
}
my $sql_insert = qq%
INSERT INTO checklist_group_mapping(col1, col2, col3)
SELECT '', gm.id, cl.id
FROM group_management AS gm
CROSS JOIN checklisst AS cl
WHERE gm.group_name IN X1
AND cl.checklist_name IN X2
%;
my $X1 = placeholder_list(scalar(@selectbox1));
my $X2 = placeholder_list(scalar(@selectbox2));
$sql_insert =~ s/X1/$X1/;
$sql_insert =~ s/X2/$X2/;
my $ins = $dbslave->prepare($sql_insert);
$ins->execute(@selectbox1, @selectbox2);
这样做的最大优点是,在应用程序和数据库之间流动的信息往返次数要少得多,这(几乎(总是能提高性能,而且通常是显著的。
唯一的问题是你的DBMS是否支持这样的显式交叉联接。 如果没有,则需要将单词 CROSS JOIN 替换为单个逗号。
还有一些事情需要修复,例如检查准备好的语句是否已成功准备,等等。 但是这可能已经让你对如何考虑将DBI与Perl一起使用有一些了解。
诀窍是在 foreach 中使用 $_ 变量。喜欢这个:
my $current_value;
foreach $select2(@selectbox2) {
$current_value = $_;
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
......
my $box_2 = $current_value;