Perl - 数据比较花费了大量时间

时间:2013-04-11 15:38:00

标签: perl

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,第二个是ALL列名,中间有逗号,第三个包含要运行的sql。由于这种能力正在进行数据比较,因此每个表名都有两行。每个DB一个。因此需要从每个相应的数据库中挑选数据,然后逐列进行比较。

SQL在结果集中作为ID返回,如果值来自db,则需要通过从数组中读取来将其转换为字符串(该数组包含带有Key的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 ......    

元数据数组包含这样的内容(每个数据库有两个)

111||Code 1    
222||Code 2    

请建议

2 个答案:

答案 0 :(得分:2)

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


摘录

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

过于复杂:不是每个人都知道C-ish for(;;)成语。你有很多代码重复。你实际上不是在说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! (只是一个提示:my的词法变量可以比全局变量稍微好一点)

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

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;

这可以让你摆脱你不需要的奇怪下标,只会减慢你的速度。

然后您分配到刚宣布的<{1}}变量。这会遮蔽外部$strvalue1。它们不是同一个变量。因此,在$strvalue1分支之后,您仍然在if中有空字符串。

我会把这段代码写成

$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


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


关于元数据:100K记录很多,所以要么把它放在数据库本身,要么至少是一个哈希。特别是对于这么多记录,使用散列比循环遍历所有条目并使用慢正则表达式快得多!aargh!

  1. 在程序开头一次从文件中创建哈希

    $x++
  2. 只需查看循环内的键

    即可
    my %metadata;
    while (<METADATA>) {
       chomp;
       my ($key, $value) = split /\|\|/;
       $metadata{$key} = $value; # assumes each key only has one value
    }
    
  3. 那应该快得多。

    (哦,请考虑为变量使用更好的名称。my $strvalue1 = defined $val1 ? $metadata{$val1} // '' : '' 不会告诉我任何内容,除了它是一个字符串值(d'oh)。$strvalue1更糟糕。)

答案 1 :(得分:2)

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

在内部for循环内,有:

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

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

稍后,您检查if (defined @val1)?你真的想检查什么?正如perldoc -f defined所指出的那样:

  

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

在您的情况下,if (defined @val1)将永远为真。

然后,您有my @filtered = grep /@val1/, @metadata; @metadata来自哪里?你究竟打算检查什么?

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

那里有一些有趣的东西。

您需要用语言描述您实际上要做的事情。

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