2010-06-23 1 views
5

Potrebbe essere una domanda per principianti, ma ancora ..Tabella di ricerca per decodifiche oracle?

Tutti conosciamo i decodificatori ei casi Oracle, ad es.

select 
    decode (state, 
      0, 'initial', 
      1, 'current', 
      2, 'finnal', 
      state) 
from states_table 

Oppure lo stesso genere di utilizzo di CASE.

Ora diciamo che ho una tabella con questi stessi valori:

state_num | state_desc 
     0 | 'initial' 
     1 | 'current' 
     2 | 'finnal' 

c'è un modo ho potuto fare la stessa query utilizzando questa tabella come una risorsa per la decodifica? Si prega di notare che non voglio congiungere la tabella per accedere ai dati dall'altra tabella ... voglio solo sapere se c'è qualcosa che potrei usare per fare una sorta di decode(myField, usingThisLookupTable, thisValueForDefault).

risposta

3

Invece di un join, è possibile utilizzare una sottoquery, vale a dire

select nvl(
    (select state_desc 
    from lookup 
    where state_num=state),to_char(state)) 
from states_table; 
+0

È vero, è possibile. Ma non avvantaggiabile. Però riformulerò la mia risposta :-) –

+0

Questo lo farebbe. Non ho molta familiarità con i compromessi di questa pratica, ma se una piccola domanda che ho a che fare con una decodifica enorme lo renderebbe molto disordinato :(@ammoQ, avresti un suggerimento per un valore predefinito? Cheers! – filippo

+0

EDIT : un po 'di più per fornire un valore predefinito –

2

No, non esiste un altro modo, oltre all'utilizzo di un join per il secondo tavolo. Certo, potresti scrivere una subquery scalare nella tua clausola select, o potresti scrivere la tua funzione, ma sarebbe una pratica inefficiente.

Se sono necessari i dati dalla tabella, è necessario selezionarli.

MODIFICA: Devo perfezionare la mia precedente affermazione sulla pratica inefficiente.

Quando si utilizza una sottoquery scalare nell'elenco di selezione, ci si aspetterebbe che si stia costringendo un piano simile a un loop nidificato, in cui la sottoquery scalare viene eseguita per ogni riga dello states_table. Almeno me l'aspettavo :-).

Tuttavia, Oracle ha implementato il caching delle subquery scalari, il che porta ad un'ottimizzazione davvero interessante. Esegue la subquery solo 3 volte. C'è un eccellente articolo sulle subquery scalari in cui si può vedere che più fattori giocano un ruolo nel modo in cui si comporta questa ottimizzazione: http://www.oratechinfo.co.uk/scalar_subqueries.html#scalar3

Ecco il mio test per vedere questo al lavoro. Per una simulazione delle tabelle, ho usato questo script:

create table states_table (id,state,filler) 
as 
select level 
     , floor(dbms_random.value(0,3)) 
     , lpad('*',1000,'*') 
    from dual 
connect by level <= 100000 
/
alter table states_table add primary key (id) 
/
create table lookup_table (state_num,state_desc) 
as 
select 0, 'initial' from dual union all 
select 1, 'current' from dual union all 
select 2, 'final' from dual 
/
alter table lookup_table add primary key (state_num) 
/
alter table states_table add foreign key (state) references lookup_table(state_num) 
/
exec dbms_stats.gather_table_stats(user,'states_table',cascade=>true) 
exec dbms_stats.gather_table_stats(user,'lookup_table',cascade=>true) 

Quindi eseguire la query e hanno uno sguardo alla vera piano di esecuzione:

SQL> select /*+ gather_plan_statistics */ 
    2   s.id 
    3  , s.state 
    4  , l.state_desc 
    5 from states_table s 
    6   join lookup_table l on s.state = l.state_num 
    7/

     ID  STATE STATE_D 
---------- ---------- ------- 
     1   2 final 
... 
    100000   0 initial 

100000 rows selected. 

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')) 
    2/

PLAN_TABLE_OUTPUT 
-------------------------------------------------------------------------------------------------------------------------------------- 
SQL_ID f6p6ku8g8k95w, child number 0 
------------------------------------- 
select /*+ gather_plan_statistics */  s.id  , s.state  , l.state_desc from states_table s  join 
lookup_table l on s.state = l.state_num 

Plan hash value: 1348290364 

--------------------------------------------------------------------------------------------------------------------------------- 
| Id | Operation   | Name   | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem | 
--------------------------------------------------------------------------------------------------------------------------------- 
|* 1 | HASH JOIN   |    |  1 | 99614 | 100K|00:00:00.50 | 20015 | 7478 | 1179K| 1179K| 578K (0)| 
| 2 | TABLE ACCESS FULL| LOOKUP_TABLE |  1 |  3 |  3 |00:00:00.01 |  3 |  0 |  |  |   | 
| 3 | TABLE ACCESS FULL| STATES_TABLE |  1 | 99614 | 100K|00:00:00.30 | 20012 | 7478 |  |  |   | 
--------------------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    1 - access("S"."STATE"="L"."STATE_NUM") 


20 rows selected. 

Ora fare lo stesso per la variante subquery scalare:

SQL> select /*+ gather_plan_statistics */ 
    2   s.id 
    3  , s.state 
    4  , (select l.state_desc 
    5    from lookup_table l 
    6   where l.state_num = s.state 
    7  ) 
    8 from states_table s 
    9/

     ID  STATE (SELECT 
---------- ---------- ------- 
     1   2 final 
... 
    100000   0 initial 

100000 rows selected. 

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')) 
    2/

PLAN_TABLE_OUTPUT 
-------------------------------------------------------------------------------------------------------------------------------------- 
SQL_ID 22y3dxukrqysh, child number 0 
------------------------------------- 
select /*+ gather_plan_statistics */  s.id  , s.state  , (select l.state_desc 
from lookup_table l   where l.state_num = s.state  ) from states_table s 

Plan hash value: 2600781440 

--------------------------------------------------------------------------------------------------------------- 
| Id | Operation     | Name   | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | 
--------------------------------------------------------------------------------------------------------------- 
| 1 | TABLE ACCESS BY INDEX ROWID| LOOKUP_TABLE |  3 |  1 |  3 |00:00:00.01 |  5 |  0 | 
|* 2 | INDEX UNIQUE SCAN   | SYS_C0040786 |  3 |  1 |  3 |00:00:00.01 |  2 |  0 | 
| 3 | TABLE ACCESS FULL   | STATES_TABLE |  1 | 99614 | 100K|00:00:00.30 | 20012 | 9367 | 
--------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - access("L"."STATE_NUM"=:B1) 


20 rows selected. 

E guardare la colonna Inizio dei passaggi 1 e 2: solo 3!

Se questa ottimizzazione è sempre una buona cosa nella vostra situazione, dipende da molti fattori. Puoi fare riferimento all'articolo precedente per vedere l'effetto di alcuni.

Nella tua situazione con solo tre stati, sembra che non ci si possa sbagliare con la variante di subquery scalare.

Cordiali saluti, Rob.

+0

sì gemella ..questo è quello che non voglio come sottolineato in "Si noti che non voglio congiungere il tavolo per accedere ai dati dall'altra tabella ..." La situazione è che sono bloccato in un sistema che mi permetterà inserire i parametri per ogni campo che desidero, ma non mi consente di modificare il resto della query. Ecco perché posso usare una decodifica, ma non posso usare un join. – filippo

+0

Ho modificato la mia risposta perché ho smesso di leggere troppo presto. –

+0

-1 per "pratica inefficiente". In pratica l'overhead è molto piccolo e vale la pena di bilanciare il codice rendendolo comprensibile, il che è un enorme vantaggio. C'è stata una grande discussione sul supporto enum nei database relazionali - prova google per un misto di discussioni informate e sbagliate. – symcbean