3

伙计们。说,我有一个问题:

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视为资源消耗。我假设,在第二个查询中,它的调用次数减少了两次,但在此更改后我没有体验到任何显着的性能提升。

所以,我想,我的假设是错误的。那么 Oracle 是如何实际处理这些函数调用的呢?

4

4 回答 4

4

这是一个非常好的问题。

我首先尝试创建表并插入示例数据(仅五行):

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 优化器模式):

--------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
--------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |       |       |       |
|*  1 |  TABLE ACCESS FULL   | MY_TABLE    |       |       |       |
--------------------------------------------------------------------

问题是:为什么计数(SELECT)= 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)它解决了数据库本身。

于 2010-09-08T00:29:59.910 回答
1

在这两种情况下,该函数都会为 中的每一行调用一次my_table。在第一种情况下,调用将作为where子句的结果,并且它刚刚找到的值将被返回,而无需再次计算。在第二种情况下,所有计算值将从子查询返回,然后由外部查询的where子句过滤。

编辑:根据马丁的测试,显然不是真的。现在我必须回去找到我多年前所做的测试,让我认为是这种情况,看看我做错了什么。关于联邦调查局的那一点仍然是真实的。我希望。

内存使用和优化器使用的确切计划可能存在一些细微差别,但我认为两者都不重要。几乎可以肯定不会反对函数调用本身的成本。

我能看到优化它的唯一方法是使用基于函数的索引。

于 2010-09-07T09:06:52.517 回答
0

您可以使用 PL/SQL pragma 来影响 oracle 优化查询的方式,请参阅RESTRICT_REFERENCES Pragma

于 2010-09-08T08:38:12.277 回答
0

一个简单的测试:

create or replace function print_function(v1 number) return number is
begin
   dbms_output.put_line(v1);
   return v1;
end;
/

select print_function(ASCII(dummy)) as test
  from dual
 where chr(print_function(ASCII(dummy))) = dummy;

结果(使用 10g):

      TEST
----------
        88

88
88

结论:该函数在 SELECT 和 WHERE 子句中分别执行。

于 2010-09-07T17:51:44.237 回答