Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/spring-mvc/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oracle如何处理SQL中存储的函数调用?_Sql_Oracle_Performance_Stored Functions - Fatal编程技术网

Oracle如何处理SQL中存储的函数调用?

Oracle如何处理SQL中存储的函数调用?,sql,oracle,performance,stored-functions,Sql,Oracle,Performance,Stored Functions,伙计们。我有一个疑问: 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

伙计们。我有一个疑问:

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
让我们把我的存储函数看作是消耗资源的函数。我假设,在第二个查询中,调用它的次数减少了两次,但在这次更改之后,我没有体验到任何显著的性能提高

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

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

编辑:根据马丁的测试,显然不是这样。现在我必须回到过去,找到几年前我做的测试,让我认为这是事实,看看我做错了什么。关于FBI的一点仍然是正确的。我希望如此。

内存使用和优化器使用的确切计划可能会有一些细微的差异,但我认为这两个方面都不重要。几乎可以肯定,这与函数调用本身的成本无关

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

一个简单的测试:

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子句中分别执行。

这是一个非常好的问题

我首先尝试创建表并仅插入五行样本数据:

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;
/
现在,我们可以在11g上运行Oracle 9i上的测试,结果相同:

-- 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
DBMS Output (Session: [1] SCOTT@ORA9i at: 08.09.2010 02:08:04): 
-----------------------------------------------------------------------
Count (SELECT) = 8
Count (WHERE) = 0
以下是计划表:

--------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
--------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |       |       |       |
|*  1 |  TABLE ACCESS FULL   | MY_TABLE    |       |       |       |
--------------------------------------------------------------------
这意味着在全表扫描的情况下,为表的每一行调用WHERE calues函数。在SELECT语句中,启动的次数与my_function=1的条件相同

现在。。。在Oracle9i和11g上测试第二个查询的相同结果

结果是:

DBMS Output (Session: [1] SCOTT@ORA9i at: 08.09.2010 01:50:04): 
-----------------------------------------------------------------------
Count (SELECT) = 3
Count (WHERE) = 5
DBMS Output (Session: [1] SCOTT@ORA9i at: 08.09.2010 02:08:04): 
-----------------------------------------------------------------------
Count (SELECT) = 8
Count (WHERE) = 0
对于“选择优化器”模式,请简单地解释如下:

--------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
--------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |       |       |       |
|*  1 |  TABLE ACCESS FULL   | MY_TABLE    |       |       |       |
--------------------------------------------------------------------
问题是:为什么计数选择=8

因为Oracle在我的情况下首先使用完整表扫描运行子查询,所以它的5行=5调用SELECT语句中的my_函数:

select t.value, my_package.my_function(t.value, 'SELECT') func_value from my_table t
对于此视图,由于subquery.func_value=1的条件,子查询类似于视图运行3次,再次调用函数my_函数

我个人不建议在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上的结果为:

在Oracle 11g上:


在这种情况下,这表明有时函数的使用可能对性能至关重要。在其他情况下,11g解决了数据库本身。

您可以使用PL/SQL pragmas来影响oracle优化查询的方式,请参见

,要实现基于函数的索引,存储的函数必须是确定性的。ha!有趣的是,如果我查询'select t1.value from select t.value,my_package.my_function t.value',其中'fv from my_table t t1,其中t1.fv=1',而没有在输出字段中实际列出我的fv列,那么它会返回到5。我认为oracle只是将子查询级别的函数调用结果传播到了更高的级别,确实如此,但方式很奇怪。其中t1.fv=1不会再次调用函数,但在所选列列表中列出t1.fv会再次调用函数。是的,最佳解决方案是:从my_表t中选择t.value,n_一些所需值,其中my_存储了函数t.value=n_一些所需值;问题是,在现实中,我必须检查我存储的函数是否不等于零,并输出它的实际值,如果它不等于零:那么在何处使用它而不使用函数呢。。。当t.value!=0然后是t值,否则为0结束;在这个测试中,您使用了函数的第二个参数来区分这两个调用。因此,这不是对两个调用相同时可能发生的情况的有效测试。