sql >> Databáze >  >> RDS >> Oracle

Jak Oracle zpracovává volání uložených funkcí v SQL?

To je opravdu dobrá otázka.

Nejprve jsem zkusil vytvořit tabulku a vložit ukázková data (pouze pět řádků):

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);

Pro testování jsem vytvořil jednoduchý testovací balíček.

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;
/

A tělo...

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;
/

Nyní můžeme spustit test na Oracle 9i (na 11g jsou stejné výsledky):

-- 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);

Výsledek je:

DBMS Output (Session: [1] [email protected] at: 08.09.2010 01:50:04): 
-----------------------------------------------------------------------
Count (SELECT) = 3
Count (WHERE) = 5

Zde je tabulka plánu:

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

Což znamená, že funkce (v calues ​​WHERE) je volána pro každý řádek tabulky (v případě FULL TABLE SCAN). V příkazu SELECT je spuštěn právě tolikrát, kolikrát je splněna podmínka WHERE my_function =1

Nyní... otestujte svůj druhý dotaz (stejné výsledky na Oracle9i a 11g)

Výsledek je:

DBMS Output (Session: [1] [email protected] at: 08.09.2010 02:08:04): 
-----------------------------------------------------------------------
Count (SELECT) = 8
Count (WHERE) = 0

Vysvětlete jednoduchý vzhled takto (pro režim CHOOSE optimalizátoru):

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

OTÁZKA JE:Proč počítat (SELECT) =8?

Protože Oracle nejprve spustil poddotaz (v mém případě s FULL TABLE SCAN je to 5 řádků =5 volání my_function v příkazu SELECT):

select t.value, my_package.my_function(t.value, 'SELECT') func_value from my_table t

A pak pro tento pohled (poddotaz je jako pohled) spusťte 3x (kvůli podmínce, kde subquery.func_value =1) znovu zavolejte funkci my_function.

Osobně nedoporučuji používat funkci v klauzuli WHERE, ale uznávám, že někdy je to nevyhnutelné.

Jako nejhorší možný příklad je to ilustrováno následujícím:

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');

Kde je výsledek na Oracle 9i :

Count (SELECT) = 5
Count (WHERE) = 50

A na Oracle 11g je :

Count (SELECT) = 5
Count (WHERE) = 5

Což v tomto případě ukazuje, že někdy může být použití funkcí kritické pro výkon. V ostatních případech (11g) řeší samotnou databázi.



  1. SQLSTATE[HY000] [2002] Žádná taková chyba souboru nebo adresáře při migraci tabulky v Laravelu

  2. Problém s ukládáním hodnot zeměpisné šířky a délky do databáze MySQL

  3. Připojit SQL Server 2008 ke kalendáři aplikace Outlook?

  4. Jak důležité je pořadí sloupců v indexech?