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.
È vero, è possibile. Ma non avvantaggiabile. Però riformulerò la mia risposta :-) –
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
EDIT : un po 'di più per fornire un valore predefinito –