Oracle
 sql >> база данни >  >> RDS >> Oracle

Как Oracle обработва съхранени извиквания на функции в SQL?

Това е наистина добър въпрос.

Първо се опитах да създам таблица и да вмъкна примерни данни (само пет реда):

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] [email protected] 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 calues) се извиква за всеки ред от таблицата (в случай на FULL TABLE SCAN). В оператора SELECT се стартира точно толкова пъти, че отговаря на условието WHERE my_function =1

Сега... тествайте втората си заявка (същите резултати за Oracle9i и 11g)

Резултатът е:

DBMS Output (Session: [1] [email protected] 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 извиквания my_function в израза SELECT):

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) решава самата база данни.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Добавете 2 месеца към текущото времеви печат

  2. Oracle:Извикване на съхранена процедура вътре в пакета

  3. Използване на връзки към бази данни на Oracle без нечетлив динамичен SQL

  4. ORACLE SQL Диапазон от часове

  5. правейки моята таблична форма динамична