优化从perl调用的sql查询

时间:2011-08-29 18:01:49

标签: regex perl query-optimization postgresql

我一直在努力尝试优化几个查询消耗的时间和资源。我从perl脚本调用。问题如下:

预期行为 有一个包含超过4百万个寄存器的表,其中一个字段(我将其称之为f_path)此表是一个字符串,表示目录路径可能包含也可能不包含(大多数情况下)包含子字符串的字符串日期格式。脚本期望的行为是获取这些寄存器的子集,使用f_path中类似日期的子字符串对其进行过滤,并将它们存储在本地文件中以对它们执行某些操作并在某些图形生成中使用这些数据功能。过滤器的工作方式如下:获取f_path中具有对应日期子字符串的所有记录,这些记录位于用户定义的日期范围内。用户定义此范围,提供最后日期(要提取的最新日期)和表示从上一个日期向后提取的日期数的数字。日期遵循一个有点连续的模式,但不能指望(可能存在差距)。符合用户提供的过滤器参数的所有日期都将存储在perl,文件或两者中的变量中,因为它们稍后将被其他脚本使用,并且还用于控制提到的图形函数prevously。

当前行为: 没有过滤。当前查询获取所有400万个+寄存器并将它们存储在本地文件中,然后它在同一个表上运行另一个查询,该查询获取f_path的所有不同值(可以选择具有相同日期子字符串的几个值)即使日期相同,目录路径也可以是不同的)并将它们存储在perl中的局部变量中,稍后应用正则表达式来获取类似日期的子字符串并将它们存储在文件中。问题:这是非常低效的,因为它会查询整个表两次并恢复大量不会被使用的数据。

尝试修复: 遗憾的是,我所尝试过的所有内容都比当前的脚本更慢:

  • 使用子字符串函数,正则表达式和用户提供的过滤器参数,从select操作到包含所有数据的表创建临时表,以获取日期集(而不是整个目录路径,即value f_path)用作过滤器。然后在f_path字段(实际上称为proj_base)上使用所需数据在此表和表之间建立连接,应用substring函数获取类似日期的子字符串并验证它是否与其中的任何值匹配临时表。这种方法的另一个缺点是,与当前脚本一样,我将不得不在稍后的脚本中再次查询表,以获取充当过滤器的日期集。我可能能够使用我创建的临时表,但是当我再次查询日期时,我不确定它是否仍然存在,没有尝试过。它看起来像这样:

psql -U postgres -c "CREATE TEMP TABLE temp_filter_trees AS SELECT DISTINCT substring(proj_base, '[0-9]{4}_[0-9]{2}_[0-9]{2}') AS "TREE" FROM testcase ORDER BY TREE DESC LIMIT 30; SELECT mem_tot_size, mem_size_inc_pct, tot_sim_time, duration, cpu_util_pct, cpu_tot_time, result, test_host, proj_base, topology FROM testcase, temp_filter_trees WHERE substring(proj_base, '[0-9]{4}_[0-9]{2}_[0-9]{2}') = temp_filter_trees.TREE " $proj > $dir/log_file;

  • 使用子字符串函数和正则表达式查询表中所有符合过滤器参数的日期,并将它们存储在列表变量中。然后循环将正则表达式应用于此列表变量的字段以确定它仅包含日期,并构建一个正则表达式,其中所有日期都将用作查询中的过滤器参数。使用从循环中获取的模式作为获取所有数据的查询中的过滤器参数。它看起来像这样:

@data = psql -U postgres -c "SELECT DISTINCT substring(proj_base, '[0-9]{4}_[0-9]{2}_[0-9]{2}') AS "TREE" FROM testcase ORDER BY TREE DESC LIMIT 30" $proj;

foreach(@data)
{ 
next unless m/(\d{4}\w{1}\d{2}\w{1}\d{2})$/; 
if(defined $psqlpattern){
$psqlpattern = $psqlpattern ."|$1";
}
else{
$psqlpattern = "'" . $1;
}
push @trees, $1;

}

$ psqlpattern = $ psqlpattern。 “'”;

psql -U postgres -c "SELECT mem_tot_size, mem_size_inc_pct, tot_sim_time, duration, cpu_util_pct, cpu_tot_time, result, test_host, proj_base, topology FROM testcase WHERE proj_base ~ $psqlpattern" $proj > $dir/log_file;

其他想法:我还想过在查询中的f_path字段上使用子字符串查询来获取所有数据,以检查类似日期的子字符串是否在由指定的范围内用户通过询问它是否比过去的日期更早且比过滤器中的旧日期更新,无论如何必须先前获取。我没有试过这个,但似乎比我尝试的其他事情更有效率。

我们将非常感谢您提供的任何见解或帮助。谢谢你的时间!

1 个答案:

答案 0 :(得分:1)

读取记录的perl i / o操作非常昂贵,尤其是对于4M记录。因此,我建议的一般方法是在数据库中尽可能多地完成工作,并专注于调优数据库查询。我也会尝试通过一次传递来实现这一点,并避免将记录复制到临时表。

所以作为一个起点,也许看看你是否可以通过使用psql的SIMILAR TO运算符或可能regexp_matches函数的单个查询来实现你想要的。然后根据日期添加另一个条件进行过滤。