我有两个问题:
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)
作为变量?
答案 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=2012
(termout
更改只会使实际检索变得不可见,并且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在这种情况下做了什么。