如何将数组的当前值存储在Perl for循环内的变量中

时间:2013-07-06 11:51:02

标签: arrays perl cgi

我的要求是,我想将一些清单值映射到某些组。以下是我的代码:

@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();
      }
}

如何将数组的当前值分配给变量,以便将其插入到映射表中?

2 个答案:

答案 0 :(得分:3)

您需要阅读'上下文',特别是'标量上下文'和'数组上下文'。

当你写:

  my $box_1 = @box1;

您正在提供标量上下文,在标量上下文中,@box1返回数组中的元素数。如果你写了:

  my($box_1) = @box1;

您将提供数组上下文,并且在数组上下文中,@box1的第一个元素将被分配给数组上下文的第一个元素$box_1 - 以及{{1}的其余元素将被删除。 (这可能就是您所追求的;您可能正在尝试为@box1中的每个名称选择单个ID值。)

根据您在代码中尝试使用@selectbox1$box_1变量的方式判断,您希望获得一个包含$box_2和另一个值的所有值的字符串包含@box1中所有值的字符串,它们可能需要显示在用单引号括起来的DBI驱动程序中。

您可以使用以下方法将空格分隔的值转换为字符串:

@box2

如果您需要以逗号分隔的值,可以使用:

my $box_1 = "@box1";

my $box_1; { local $" = ","; $box_1 = "@box_1"; } $"下的$LIST_SEPARATOR)必须进行本地化以防止损坏,但这意味着您必须将use English '-no_match_vars';的定义与作业分开(因为如果不这样做,$box_1会在您离开$box_1区块时被销毁。

现在,为了保护SQL以便SQL可以工作,您需要使用quote方法:

{...}

或:

$box1 = $dbslave->quote($box1);

汇总这些变化,我们得到:

my $box1 = $dbslave->quote("@box1");

请注意,两个SELECT语句假定选择框字符串不包含任何有趣的字符(特别是没有单引号)。如果您负责#!/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(); } } @selectbox1的内容,那就没问题。如果它们包含用户输入,则必须清理该输入,或再次使用@selectbox2或使用占位符。我将忽略这个问题。

您还在使用带有$dbslave->quote()的标量上下文,这不会产生您想要的答案(尽管$box1->fetchrow_array()对上下文敏感,manual警告您要小心)。我会使用类似的东西:

fetchrow_array()

您还需要使用功能。您的代码中有一个明显的重复,可以封装到使用两次的单个函数中:

    my @box1;
    while (my @row = $box1->fetchrow_array())
    {
        push @box1, $row[0];
    }
    my $box_1 = $dbslave->quote("@box1");

应该将INSERT语句转换为使用占位符,以便可以准备一次并多次使用:

#!/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();
    }
}

实际上,两个SELECT语句也应该参数化并准备一次并重复使用。我没有表现出这种变化,因为(a)我很懒,而且(b)有一个更大的变化,更有效。

当我们看看你真正在做什么时,它应该都是一个单独的SQL语句:

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);
    }
}

这样做的最大优点是,在应用程序和数据库之间传递信息的往返次数要少得多,这几乎总是能够显着提高性能。

唯一剩下的问题是你的DBMS是否支持这样的显式CROSS JOIN。如果没有,你需要用一个逗号替换单词CROSS JOIN。

仍有一些问题需要修复,例如检查准备好的语句是否已成功准备,等等。但这可能让您对如何考虑将DBI与Perl一起使用有所了解。

答案 1 :(得分:-4)

诀窍是在 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;