Non, il n'y a pas d'autre moyen, en plus d'utiliser une jointure à votre deuxième table. Bien sûr, vous pourriez écrire une sous-requête scalaire dans votre clause select, ou vous pourriez écrire votre propre fonction, mais ce serait une pratique inefficace.
Si vous avez besoin des données de la table, vous devez en choisir une.
EDIT: Je dois affiner ma déclaration précédente sur la pratique inefficace. Lorsque vous utilisez une sous-requête scalaire dans votre liste de sélection, vous vous attendez à ce que vous forçiez un plan semblable à une boucle imbriquée, où la sous-requête scalaire est exécutée pour chaque ligne de la table states_table. Au moins, je m'attendais à ça :-). Toutefois, Oracle a implémenté la mise en cache des sous-requêtes scalaires, ce qui conduit à une optimisation très intéressante. Il n'exécute que la sous-requête 3 fois. Il y a un excellent article sur les sous-requêtes scalaires où vous pouvez voir que plus de facteurs jouent un rôle dans la façon dont cette optimisation se comporte: http://www.oratechinfo.co.uk/scalar_subqueries.html#scalar3
Voici mon propre test pour voir cela au travail. Pour une simulation de vos tables, je ce 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)
Ensuite, exécutez la requête et un regard sur le réel plan d'exécution:
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.
maintenant la même chose pour la variante de sous-requête scalaire:
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.
Et regardez la colonne Démarrage des étapes 1 et 2: seulement 3! Si cette optimisation est toujours une bonne chose dans votre situation, cela dépend de nombreux facteurs.
Vous pouvez vous référer à l'article mentionné précédemment pour voir l'effet de certains.
Dans votre situation avec seulement trois états, il semble que vous ne pouvez pas vous tromper avec la variante de sous-requête scalaire.
Cordialement, Rob.
C'est vrai, c'est possible. Mais pas utile. Je vais reformuler ma réponse si :-) –
Cela ferait juste ça. Je ne suis pas trop familier avec les compromis de cette pratique, mais si une petite question que je traite où un décodage massif rendrait très désordonné :(@ammoQ, auriez-vous une suggestion pour une valeur par défaut? Applaudissements! – filippo
EDIT : un peu plus pour donner une valeur par défaut –