2013-04-11 52 views
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,第二个是ALL列名,逗号在第三个之间,第三个包含要运行的SQL。由于此功能正在对数据进行比较,因此每个表名都有两行。每个DB一个。所以数据需要从每个数据库中挑选出来,然后逐列比较。Perl - 以大量时间进行数据比较

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)

111||Code 1  
222||Code 2  

请建议

+1

你是否通过探查器(如Devel :: NYTProf或Devel :: DProf)运行它,并消除延迟在SQL执行中的可能性? – imran 2013-04-11 15:58:40

+1

通常在循环中“准备”是一件坏事。你确定你不能在循环之外“准备”所有的查询,只是稍后用不同的参数“执行”它们? – gangabass 2013-04-11 16:00:48

+0

查询是否总是返回相同的一组列?你究竟在比较什么?代码很混乱,但是你能说出你在找什么吗?例如。 _我想要TABLENAME中所有行的列表,但不是TABLENAMEB_或类似的东西? – 2013-04-11 16:12:26

回答

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; 

并保存intendation的水平。更好的intendation等于更好的可读性使得编写好的代码更容易。而更好的代码可能表现更好。


什么是你在做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; 

这将释放你从你不需要怪异标,只有慢你失望。

然后你分配到一个$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通常写成$x++


清空在迭代结束时的hashrefs是不必要的:本hashrefs是在循环的下一次迭代分配给一个新的值。另外,为这些简单的任务协助Perls垃圾回收并不是必需的。


关于元数据:100K记录很多,所以要么把它放在数据库本身,要么至少是一个散列。特别是对于如此多的记录,使用散列的速度比使用缓慢正则表达式的循环更快 ... aargh!

  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'哦)。$val1更是雪上加霜。)

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查询,它会给你你想要的,但我们首先需要知道你想要什么。