避免对相同值进行多次计算

时间:2013-05-13 10:08:19

标签: sql performance oracle select database-performance

有没有办法可以通过避免多次计算某些重复值来提高此查询的性能

regexp_replace(my_source_file, 'TABLE_NAME_|_AM|_PM|TO|.csv|\d+:\d{2}:\d{2}', '')
? 和

to_date(regexp_replace(regexp_replace(my_source_file, 'TABLE_NAME_|_AM|_PM|_TO_|\.csv|\d+:\d{2}:\d{2}', ''), '^(\d{4}-\d+-\d+)_.+$', '\1'), 'YYYY-MM-DD')

此列计算3次和2次,我运行了一些测试,只有删除date_start列,查询性能提高了大约20秒。我在想oracle是否提供了更好的方法来保留值并避免多次计算会很棒。我也想避免

实际查询:

select * 
from (
  select
      row_number() over (partition by DCRAINTERNALNUMBER, ISSUE_DATE, PERMIT_ID order by to_date(regexp_replace(regexp_replace(my_source_file, 'TABLE_NAME_|_AM|_PM|_TO_|\.csv|\d+:\d{2}:\d{2}', ''), '^.+_(\d{4}-\d+-\d+)_$', '\1'), 'YYYY-MM-DD') desc) as row_order,
      to_date(regexp_replace(regexp_replace(my_source_file, 'TABLE_NAME_|_AM|_PM|_TO_|\.csv|\d+:\d{2}:\d{2}', ''), '^(\d{4}-\d+-\d+)_.+$', '\1'), 'YYYY-MM-DD') as date_start,
      to_date(regexp_replace(regexp_replace(my_source_file, 'TABLE_NAME_|_AM|_PM|_TO_|\.csv|\d+:\d{2}:\d{2}', ''), '^.+_(\d{4}-\d+-\d+)_$', '\1'), 'YYYY-MM-DD') as date_end,
      temp2.* 
  from schema.TABLE_NAME temp2
) t

我还尝试模拟这样的事情以避免多次计算,但是由于所有嵌套的select语句它没有改进任何东西......它使查询变慢约25秒:

select * 
from (
  select row_number() over (partition by DCRAINTERNALNUMBER, ISSUE_DATE, PERMIT_ID order by date_end desc) as row_order,
  temp1.*
  from (
      select to_date(regexp_replace(date_raw, '^(\d{4}-\d+-\d+)_.+$', '\1'), 'YYYY-MM-DD') as date_start,
          to_date(regexp_replace(date_raw, '^.+_(\d{4}-\d+-\d+)_$', '\1'), 'YYYY-MM-DD') as date_end,
      temp2.* 
      from (
      select regexp_replace(my_source_file, 'TABLE_NAME_|_AM|_PM|_TO_|\.csv|\d+:\d{2}:\d{2}', '') as date_raw,
        temp3.*
        from schema.TABLE_NAME temp3
      ) temp2

  ) temp1
) t

3 个答案:

答案 0 :(得分:2)

如果这是一个输入值很少的确定性PL / SQL函数,我当然会尝试更改:

select expensive_function(some_value)
from   large_table;

...到......

select (select expensive_function(some_value) from dual)
from   large_table;

...因为Oracle有一个缓存机制。您可以将SQL表达式推送到子查询中,如果这不起作用(或者即使它不起作用),我会将该SQL代码移动到PL / SQL函数中并尝试它。

哦,如果你当然是11g,我会直接使用pl / sql函数缓存:http://www.oracle.com/technetwork/issue-archive/2007/07-sep/o57asktom-101814.html

答案 1 :(得分:1)

它没有回答您关于减少函数调用次数的具体问题,但您是否考虑过使用regexp_substr而不是多次调用regexp_replace函数?我认为这会减少工作量,应该更快。如果数据不完全匹配(例如,如果文件名是.txt而不是.csv),它也应该不太可能给你一个例外。

像...一样的东西。

select * 
from (
  select row_number() over (partition by DCRAINTERNALNUMBER, ISSUE_DATE, PERMIT_ID order by to_date(regexp_substr(my_source_file,'\d{4}-\d{1,2}-\d{1,2}'),'yyyy-mm-dd') desc) as row_order,
  to_date(regexp_substr(my_source_file,'\d{4}-\d{1,2}-\d{1,2}'),'yyyy-mm-dd') as date_start,
  to_date(regexp_substr(my_source_file,'\d{4}-\d{1,2}-\d{1,2}',1,2),'yyyy-mm-dd') as date_end,
  temp2.* 
from schema.TABLE_NAME temp2) t

如果我已正确解释您的数据

TABLE_NAME_2011-3-1_11:00:00_AM_TO_2013-4-24_12:00:00_AM.csv

在2011-3-1是开始日期,2013-4-24是结束日期。我可以通过使用相同的模式匹配来获取这些日期,但是选择开始日期的第一个实例(不需要参数,因为这是默认值)和结束日期的第二个实例(这需要额外的,1,2为substr从头开始(字符1)并选择第二个实例。

希望有所帮助。

答案 2 :(得分:0)

不确定会有多大改善,但您可以使用CTE:

with cte as (
  select tt.*, to_date(regexp_replace(regexp_replace(my_source_file, 'TABLE_NAME_|_AM|_PM|_TO_|\.csv|\d+:\d{2}:\d{2}', ''), '^.+_(\d{4}-\d+-\d+)_$', '\1'), 'YYYY-MM-DD') calc_val
  from schema.TABLE_NAME tt
)
select * 
from (
  select
      row_number() over (partition by DCRAINTERNALNUMBER, ISSUE_DATE, PERMIT_ID order by calc_val desc) as row_order,
      calc_val as date_start,
      calc_val as date_end,
      temp2.* 
  from cte temp2
) t