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
请建议
答案 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!
在程序开头一次从文件中创建哈希
$x++
只需查看循环内的键
即可my %metadata;
while (<METADATA>) {
chomp;
my ($key, $value) = split /\|\|/;
$metadata{$key} = $value; # assumes each key only has one value
}
那应该快得多。
(哦,请考虑为变量使用更好的名称。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查询,它会给你你想要的东西,但我们首先要知道你想要什么。