'extract(来自sysdate的年份)'的速度

时间:2012-07-20 00:24:39

标签: sql oracle

我有两个问题:

with tmp as (
select asy.aim_student_id, ast.aim_test, asq.response
  from aim_student_test ast
  join aim_student_qst asq on (asq.aps_yr = ast.aps_yr and asq.aim_test = ast.aim_test and asq.aim_id = ast.aim_id)
  join aim_student_yr asy on (asy.aps_yr = ast.aps_yr and asy.aim_student_yr_id = ast.aim_student_yr_id)
    where asq.aps_yr = '2012'
    and asq.qst_num = 1)
select aim_student_id, aim_test, response  
  from tmp
  where response is null
  -- execution-time: 0.032 seconds


define this_year = extract(year from sysdate)
with tmp as (
select asy.aim_student_id, ast.aim_test, asq.response
  from aim_student_test ast
  join aim_student_qst asq on (asq.aps_yr = ast.aps_yr and asq.aim_test = ast.aim_test and asq.aim_id = ast.aim_id)
  join aim_student_yr asy on (asy.aps_yr = ast.aps_yr and asy.aim_student_yr_id = ast.aim_student_yr_id)
    where asq.aps_yr = &this_year
    and asq.qst_num = 1)
select aim_student_id, aim_test, response  
  from tmp
  where response is null
  -- execution-time: 82.202 seconds

唯一的区别是,在一个中我使用'2012'而另一个我实现了extract(来自sysdate的年份)。

我只能想象Oracle正在为它检查的每条记录计算摘录(来自sysdate的年份),并且我无法弄清楚如何使它计算一次并将其用作变量。搜索没有找到我寻求的答案......所以我来到了SO.com的魔术师。我如何正确使用

extract(year from sysdate)

作为变量?

2 个答案:

答案 0 :(得分:3)

在查询中使用&this_year会导致替换字符串 extract(year from sysdate),因此第二个查询实际上有:

where asq.aps_yr = extract(year from sysdate)

您可以从第二个解释计划中看到。这本身可能不是问题;可能会减慢速度的是,这样做会将计划从index range scan更改为index skip scan而不是aim_student_qstp1。真正的区别在于,在快速版本中,您将asq.aps_yr与字符串('2012')进行比较,在第二个版本中,它是一个数字(2012),并且 - 也显示在解释计划 - 这导致它执行to_number(asq.aps_yr),这会停止按预期使用索引。

您可以通过以下方式在代码中解决此问题:

where asq.aps_yr = to_char(&this_year)

如果要在查询运行之前计算一次,然后将其用作变量,则至少有两种方法(在SQL * Plus / SQL Developer中)。坚持使用替换变量,您可以使用column命令而不是define

column tmp_this_year new_value this_year
set termout off
select extract(year from sysdate) as tmp_this_year from dual;
set termout on
set verify off

...这使得&this_year=2012termout更改只会使实际检索变得不可见,并且verify会在它使用替换时停止告诉您;两者都让你不知道在脚本中获得额外的输出,并将您的查询更改为:

where asq.aps_yr = '&this_year'

...因此该值被视为字符串,不需要to_char()

或者您可以使用绑定变量:

var this_year varchar2(4);
set feedback off;
exec :this_year := extract(year from sysdate);

...然后你的查询有:

where asq.aps_yr = :this_year

请注意,在这种情况下,您不需要引号,因为绑定变量已经定义为字符串 - 在设置它的exec中有隐式转换。

答案 1 :(得分:2)

我怀疑差异是由于从该日期开始提取年份。我很确定Oracle只会提取一年,因为它在第二种情况下使用了一个变量。

不同之处在于查询使用的执行路径。您需要发布执行计划才能真正看到差异。使用显式常量可为优化程序提供更多信息,以便选择最佳查询计划。

例如,如果数据按年分区,那么在年份不变的情况下,Oracle可以确定哪个分区具有数据。在第二种情况下,Oracle可能无法将该值识别为常量,并且需要读取所有数据分区。这只是可能发生的事情的一个例子 - 我不确定Oracle在这种情况下做了什么。