伙计们。说,我有一个查询:
select t.value, my_stored_function(t.value) from my_table t where my_stored_function(t.value) = n_Some_Required_Value
我用以下方式重写了它:
select value, func_value from (select t.value, my_stored_function(t.value) func_value from my_table t) subquery where subquery.func_value = n_Some_Required_Value
让我们将其my_stored_function视为消耗资源的一种。我认为,在第二个查询中,它的调用次数要少两次,但是在进行此更改后,我并没有发现任何显着的性能提升。
my_stored_function
因此,我想我的假设是错误的。那么,Oracle实际如何处理这些函数调用?
这是一个非常好的问题。
我首先尝试创建表并插入示例数据(仅五行):
create table my_table(value number); insert into my_table(value) values(1); insert into my_table(value) values(2); insert into my_table(value) values(3); insert into my_table(value) values(4); insert into my_table(value) values(5);
我制作了一个简单的测试包对此进行了测试。
create or replace package my_package is g_counter_SELECT PLS_INTEGER := 0; -- counter for SELECT statement g_counter_WHERE PLS_INTEGER := 0; -- counter for WHERE clause function my_function(number_in in number, type_in in varchar2) return number; procedure reset_counter; end; /
和身体…
create or replace package body my_package is function my_function(number_in in number, type_in in varchar2) return number is begin IF(type_in = 'SELECT') THEN g_counter_SELECT := g_counter_SELECT + 1; ELSIF(type_in = 'WHERE') THEN g_counter_WHERE := g_counter_WHERE + 1; END IF; return mod(number_in, 2); end; procedure reset_counter is begin g_counter_SELECT := 0; g_counter_WHERE := 0; end; end; /
现在,我们可以在Oracle 9i上运行测试(在11g上是相同的结果):
-- reset counter exec my_package.reset_counter(); -- run query select t.value, my_package.my_function(t.value, 'SELECT') from my_table t where my_package.my_function(t.value, 'WHERE') = 1; -- print result exec dbms_output.put_line('Count (SELECT) = ' || my_package.g_counter_SELECT); exec dbms_output.put_line('Count (WHERE) = ' || my_package.g_counter_WHERE);
结果是:
DBMS Output (Session: [1] SCOTT@ORA9i at: 08.09.2010 01:50:04): ----------------------------------------------------------------------- Count (SELECT) = 3 Count (WHERE) = 5
这是计划表:
-------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | -------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | |* 1 | TABLE ACCESS FULL | MY_TABLE | | | | --------------------------------------------------------------------
这意味着将对表的每一行调用该函数(以WHERE量计)(对于FULL TABLE SCAN)。在SELECT语句中启动的次数与条件WHERE my_function = 1一致
现在…测试您的第二个查询 (在Oracle9i和11g上的结果相同)
DBMS Output (Session: [1] SCOTT@ORA9i at: 08.09.2010 02:08:04): ----------------------------------------------------------------------- Count (SELECT) = 8 Count (WHERE) = 0
解释如下所示(对于CHOOSE优化器模式):
问题是: 为什么计数(选择)= 8?
因为Oracle首先运行子查询(在我的情况下是FULL TABLE SCAN,所以它有5行= 5在SELECT语句中调用my_function):
select t.value, my_package.my_function(t.value, 'SELECT') func_value from my_table t
然后,此视图(子查询就像视图)运行3次(由于subquery.func_value = 1的条件),再次调用函数my_function。
我个人不建议在WHERE子句中使用函数,但是我承认有时这是不可避免的。
以下是最糟糕的示例:
select t.value, my_package.my_function(t.value, 'SELECT') from my_table t where my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE') and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE') and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE') and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE') and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE');
在Oracle 9i上的结果是 :
Count (SELECT) = 5 Count (WHERE) = 50
在Oracle 11g上是 :
Count (SELECT) = 5 Count (WHERE) = 5
在这种情况下,这表明有时使用功能对于性能至关重要。在其他情况下(11g),它可以解决数据库本身。