0
open(INFILE1,"INPUT.txt");

my $modfile = 'Data.txt';
open MODIFIED,'>',$modfile or die "Could not open $modfile : $!";   

for (;;) {
    my $line1 = <INFILE1>;
    last if not defined $line1;

    my $line2 = <INFILE1>;
    last if not defined $line2;

    my ($tablename1, $colname1,$sql1) = split(/\t/, $line1);
    my ($tablename2, $colname2,$sql2) = split(/\t/, $line2);

    if ($tablename1 eq $tablename2)
    {
        my $sth1 = $dbh->prepare($sql1);
        $sth1->execute;
        my $hash_ref1 = $sth1->fetchall_hashref('KEY');

        my $sth2 = $dbh->prepare($sql2);
        $sth2->execute;
        my $hash_ref2 = $sth2->fetchall_hashref('KEY');

        my @fieldname = split(/,/, $colname1);
        my $colcnt=0;
        my $rowcnt=0;
        foreach $key1 ( keys(%{$hash_ref1}) ) 
        {

            foreach (@fieldname)
            { 
                $colname =$_;

                my $strvalue1='';
                @val1 = $hash_ref1->{$key1}->{$colname};

                if (defined @val1)
                {
                    my @filtered = grep /@val1/, @metadata;
                    my $strvalue1 = substr(@filtered[0],index(@filtered[0],'||') + 2);
                }

                my $strvalue2='';
                @val2 = $hash_ref2->{$key1}->{$colname};
                if (defined @val2)
                {
                    my @filtered = grep /@val2/, @metadata2;
                    my $strvalue2 = substr(@filtered[0],index(@filtered[0],'||') + 2);
                }                   

                if ($strvalue1 ne $strvalue2 )
                { 
                    $colcnt = $colcnt + 1;
                    print MODIFIED "$tablename1\t$colname\t$strvalue1\t$strvalue2\n";
                }
            }
        }
        if ($colcnt>0) 
        {   
            print "modified count is $colcnt\n";
        }   

        %$hash_ref1 = ();
        %$hash_ref2 = ();

    }

该程序是读取输入文件,其中每一行包含三个由制表符分隔的字符串。第一个是 TableName,第二个是所有列名,中间有逗号,第三个包含要运行的 sql。由于此实用程序正在比较数据,因此每个表名都有两行。每个数据库一个。因此需要从每个相应的数据库中挑选数据,然后逐列进行比较。

SQL 在结果集中作为 ID 返回,如果值来自 db,则需要通过从数组中读取将其转换为字符串(该数组包含 100K 记录,键和值由 || 分隔)
现在我运行了这个一组表,每个数据库中包含 18K 条记录。每个 sql 中从 db 中选择了 8 列。因此,对于 18K 中的每条记录,然后对于该记录中的每个字段(即 8 条),此脚本都需要大量时间。

我的问题是,是否有人可以看看它是否可以改进,以便减少时间。文件内容示例

INPUT.TXT      
TABLENAME   COL1,COL2   select COL1,COL2 from TABLENAME where ......    
TABLENAMEB  COL1,COL2   select COL1,COL2 from TABLENAMEB where ......    

元数据数组包含类似这样的内容(每个 db 有两个 ie)

111||Code 1    
222||Code 2    

请建议

4

2 回答 2

2

这不是一个真正的答案,但它也不太适合评论,所以在您提供更多信息之前,这里有一些观察。

在你的内部for循环中,有:

@val1 = $hash_ref1->{$key1}->{$colname};

你的意思是@val1 = @{ $hash_ref1->{$key1}->{$colname} };

后来,你检查if (defined @val1)?你真正想检查什么?正如perldoc -f 定义的那样:

不推荐在聚合(散列和数组)上使用“定义”。它用于报告该聚合的内存是否已分配。这种行为可能会在 Perl 的未来版本中消失。您应该改为使用简单的大小测试:

在你的情况下,if (defined @val1)永远都是真的。

那么,你有my @filtered = grep /@val1/, @metadata;哪里@metadata来的?您实际上打算检查什么?

然后你有my $strvalue1 = substr(@filtered[0],index(@filtered[0],'||') + 2);

里面发生了一些有趣的事情。

你需要用语言表达你真正想要做的事情。

我强烈怀疑你可以运行一个 SQL 查询,它会给你想要的东西,但我们首先需要知道你想要什么。

于 2013-04-11T16:26:49.410 回答
2

您的代码确实看起来有点不寻常,并且可以通过使用子例程而不是仅使用循环和条件来获得清晰。这里有一些其他的建议。


摘录

for (;;) {
  my $line1 = <INFILE1>;    
  last if not defined $line1;    
  my $line2 = <INFILE1>;    
  last if not defined $line2;  
  ...;
}

过于复杂:不是每个人都知道 C-ishfor(;;)成语。你有很多代码重复。你真的不是在说loop while I can read two lines吗?

while (defined(my $line1 = <INFILE1>) and defined(my $line2 = <INFILE1>)) {
  ...;
}

是的,那条线更长,但我认为它更像是自我记录。


而不是做

if ($tablename1 eq $tablename2) { the rest of the loop }

你可以说

next if $tablename1 eq $tablename2;
the rest of the loop;

并保存一个级别的意图。更好的意图等于更好的可读性使得编写好的代码更容易。更好的代码可能会表现得更好。


你在做什么foreach $key1 (keys ...)——有些事情告诉我你没有use strict!(只是提示:词法变量 withmy可以比全局变量执行得稍好)

此外,$colname = $_出于同样的原因,在 for 循环中执行操作是一件愚蠢的事情。

for my $key1 (keys ...) {
  ...;
  for my $colname (@fieldname) { ... }
}

my $strvalue1='';
@val1 = $hash_ref1->{$key1}->{$colname};

if (defined @val1)
{
  my @filtered = grep /@val1/, @metadata;
  my $strvalue1 = substr(@filtered[0],index(@filtered[0],'||') + 2);
}

我不认为这会像认为的那样。

$hash_ref1您检索单个元素,然后将该元素分配给一个数组(多个值的集合)。

然后你调用defined了这个数组。数组不能未定义,并且您正在做的事情已被弃用。对集合调用defined函数返回有关内存管理的信息,但不指示①数组是否为空或②数组中的第一个元素是否已定义。

将数组内插到正则表达式中不太可能有用:数组的元素与 的值($"通常是空格)连接,并将生成的字符串视为正则表达式。如果存在元字符,这将造成严重破坏。

当您只需要列表的第一个值时,您可以强制列表上下文,但分配给单个标量,例如

my ($filtered) = produce_a_list;

这使您从不需要的奇怪下标中解脱出来,这只会减慢您的速度。

然后分配给刚刚声明$strvalue1的变量。这遮住了外层。它们不是同一个变量。所以在分支之后,你仍然有空字符串。$strvalue1if$strvalue1

我会像这样写这段代码

my $val1 = $hash_ref1->{$key1}{$colname};

my $strvalue1 = defined $val1
  ? do {
    my ($filtered) = grep /\Q$val1/, @metadata;
    substr $filtered, 2 + index $filtered, '||'
  } : '';

@metadata但是,如果您预先分成对并使用正确的字段测试是否相等,这将更便宜。这将消除一些仍然潜伏在该代码中的错误。


$x = $x + 1是常用的写法$x++


在迭代结束时清空 hashrefs 是不必要的:hashrefs 在循环的下一次迭代中被分配给一个新值。此外,对于这些简单的任务,没有必要协助 Perls 垃圾收集。


关于元数据:10 万条记录很多,所以要么将其放入数据库本身,要么至少是一个哈希。特别是对于如此多的记录,使用哈希比遍历所有条目和使用慢正则表达式要快得多……啊

  1. 在程序开始时从文件创建哈希

    my %metadata;
    while (<METADATA>) {
       chomp;
       my ($key, $value) = split /\|\|/;
       $metadata{$key} = $value; # assumes each key only has one value
    }
    
  2. 只需查找循环内的密钥

    my $strvalue1 = defined $val1 ? $metadata{$val1} // '' : ''
    

那应该快得多。

(哦,请考虑为变量使用更好的名称。$strvalue1除了它是一个字符串值(d'oh)之外,什么都没有告诉我。$val1更糟。)

于 2013-04-11T16:24:19.613 回答