简体   繁体   English

“提取(从日期开始的年)”的速度

[英]The speed of 'extract(year from sysdate)'

I have two queries: 我有两个查询:

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

The only difference is that in one I use '2012' and the other I implement extract(year from sysdate). 唯一的区别是,一个我使用“ 2012”,另一个我实现了extract(sysdate年)。

I can only imagine that Oracle is computing extract(year from sysdate) for EVERY record it checks, and that I just can't figure out how to make it compute this once and use it as a variable. 我只能想象Oracle正在计算提取(从sysdate年起)的每一次记录,以检查它,而我只是想不出如何使它一次计算并将其用作变量。 Searching has not returned me the answers I seek... so I come to the magicians of SO.com. 搜索并没有返回我想要的答案...所以我来找SO.com的魔术师。 HOW do I properly use 我如何正确使用

extract(year from sysdate)

as a variable? 作为变量?

Using &this_year in the query causes a substitution of the string extract(year from sysdate) , so the second query actually has: 在查询中使用&this_year导致替换字符串 extract(year from sysdate) ,因此第二个查询实际上具有:

where asq.aps_yr = extract(year from sysdate)

which you can see from the second explain plan. 您可以从第二个解释计划中看到。 That in itself probably isn't the problem; 这本身可能不是问题; what's possibly slowing it down is that doing this is changing the plan from an index range scan to an index skip scan against aim_student_qstp1 . 可能使它变慢的是,这样做是针对根据aim_student_qstp1将计划从index range scan更改为index skip scan The real difference is that in the fast version you're comparing asq.aps_yr to a string ( '2012' ), in the second it's a number ( 2012 ), and - as also shown in the explain plan - this is causing it to do to_number(asq.aps_yr) which is stopping the index being used as you expect. 真正的区别是,在快速版本中,您将asq.aps_yr与字符串( '2012' )进行比较,第二个是数字( 2012 ),并且-如解释计划中所示-这导致它执行to_number(asq.aps_yr) ,这将停止按预期使用索引。

You could fix this in your code by making it: 您可以通过以下方式在代码中解决此问题:

where asq.aps_yr = to_char(&this_year)

If you want to calculate it once before the query runs and then use it as a variable, there are at least two ways (in SQL*Plus/SQL Developer). 如果要在查询运行之前对其进行一次计算,然后将其用作变量,则至少有两种方法(在SQL * Plus / SQL Developer中)。 Sticking with substitution variables you can use the column commands instead of the define : 坚持使用替换变量,您可以使用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

... which makes &this_year=2012 (the termout changes just make the actual retrieval invisible, and the verify stops it telling you when it uses the substitution; both so you don't get extra output in your script), and change your query to have: ...,这会&this_year=2012termout更改只会使实际的检索不可见,并且verify停止它告诉您何时使用替换;这两者都不会在脚本中获得额外的输出),并更改查询拥有:

where asq.aps_yr = '&this_year'

... so the value is treated as a string, making a to_char() unnecessary. ...因此将该值视为字符串,因此不需要to_char()

Or you can use bind variables: 或者,您可以使用绑定变量:

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

... and then your query has: ...,然后您的查询具有:

where asq.aps_yr = :this_year

Note that in this case you don't need the quotes because the bind variable is defined as a string already - there's an implicit conversion in the exec that sets it. 请注意,在这种情况下,由于绑定变量已经定义为字符串,因此不需要引号exec中有一个隐式转换将其设置。

I doubt the difference is due to extracting the year from the date. 我怀疑差异是由于从日期中提取了年份。 I'm pretty sure Oracle would only be extracting the year once, since it is using a variable in the second case. 我很确定Oracle将只提取一次年份,因为它在第二种情况下使用了变量。

The difference is due to the execution path used by the query. 差异是由于查询使用的执行路径。 You would need to post the execution plans to really see the difference. 您需要发布执行计划才能真正看到差异。 Using an explicit constant gives the optimizer more information for choosing an optimal query plan. 使用显式常量为优化器提供了更多信息,供他们选择最佳查询计划。

For instance, if the data is partitioned by year, then with a constant year, Oracle can determine which partition has the data. 例如,如果按年份对数据进行分区,那么在年份固定的情况下,Oracle可以确定具有该数据的分区。 In the second case, Oracle might not recognize the value as a constant, and require reading all data partitions. 在第二种情况下,Oracle可能无法将该值识别为常量,因此需要读取所有数据分区。 This is just an example of what might happen -- I'm not sure what Oracle does in this case. 这只是可能发生的情况的一个示例-在这种情况下,我不确定Oracle会做什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM