I think a better way to see this is to keep the query constant, but change the stats. So, in 9i:
ops$tkyte@ORA9IR2> create table emp as select * from scott.emp;
Table created.
ops$tkyte@ORA9IR2> create table dept as select * from scott.dept;
Table created.
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> exec dbms_stats.set_table_stats( user, 'EMP', numrows => 10000000, numblks => 100000 );
PL/SQL procedure successfully completed.
ops$tkyte@ORA9IR2> exec dbms_stats.set_table_stats( user, 'DEPT', numrows => 4, numblks => 1 );
PL/SQL procedure successfully completed.
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> set autotrace traceonly explain
ops$tkyte@ORA9IR2> select dept.dname, emp.ename
2 from emp, dept
3 where emp.deptno(+) = dept.deptno;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=15221 Card=10000000 Bytes=420000000)
1 0 HASH JOIN (OUTER) (Cost=15221 Card=10000000 Bytes=420000000)
2 1 TABLE ACCESS (FULL) OF 'DEPT' (Cost=2 Card=4 Bytes=88)
3 1 TABLE ACCESS (FULL) OF 'EMP' (Cost=15179 Card=10000000 Bytes=200000000)
ops$tkyte@ORA9IR2> set autotrace off
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> exec dbms_stats.set_table_stats( user, 'DEPT', numrows => 10000000, numblks => 100000 );
PL/SQL procedure successfully completed.
ops$tkyte@ORA9IR2> exec dbms_stats.set_table_stats( user, 'EMP', numrows => 4, numblks => 1 );
PL/SQL procedure successfully completed.
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> set autotrace traceonly explain
ops$tkyte@ORA9IR2> select dept.dname, emp.ename
2 from emp, dept
3 where emp.deptno(+) = dept.deptno;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=20347 Card=10000000 Bytes=420000000)
1 0 HASH JOIN (OUTER) (Cost=20347 Card=10000000 Bytes=420000000)
2 1 TABLE ACCESS (FULL) OF 'DEPT' (Cost=15179 Card=10000000 Bytes=220000000)
3 1 TABLE ACCESS (FULL) OF 'EMP' (Cost=2 Card=4 Bytes=80)
ops$tkyte@ORA9IR2> set autotrace off
<b>The plan would always be that way -- in 10g however, either table can be used to drive:</b>
ops$tkyte@ORA10G> create table emp as select * from scott.emp;
Table created.
ops$tkyte@ORA10G> create table dept as select * from scott.dept;
Table created.
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> exec dbms_stats.set_table_stats( user, 'EMP', numrows => 10000000, numblks => 100000 );
PL/SQL procedure successfully completed.
ops$tkyte@ORA10G> exec dbms_stats.set_table_stats( user, 'DEPT', numrows => 4, numblks => 1 );
PL/SQL procedure successfully completed.
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> set autotrace traceonly explain
ops$tkyte@ORA10G> select dept.dname, emp.ename
2 from emp, dept
3 where emp.deptno(+) = dept.deptno;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=22579 Card=10000000 Bytes=420000000)
1 0 HASH JOIN (OUTER) (Cost=22579 Card=10000000 Bytes=420000000)
2 1 TABLE ACCESS (FULL) OF 'DEPT' (TABLE) (Cost=2 Card=4 Bytes=88)
3 1 TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=22344 Card=10000000 Bytes=200000000)
ops$tkyte@ORA10G> set autotrace off
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> exec dbms_stats.set_table_stats( user, 'DEPT', numrows => 10000000, numblks => 100000 );
PL/SQL procedure successfully completed.
ops$tkyte@ORA10G> exec dbms_stats.set_table_stats( user, 'EMP', numrows => 4, numblks => 1 );
PL/SQL procedure successfully completed.
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> set autotrace traceonly explain
ops$tkyte@ORA10G> select dept.dname, emp.ename
2 from emp, dept
3 where emp.deptno(+) = dept.deptno;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=22579 Card=10000000 Bytes=420000000)
1 0 HASH JOIN (RIGHT OUTER) (Cost=22579 Card=10000000 Bytes=420000000)
2 1 TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=2 Card=4 Bytes=80)
3 1 TABLE ACCESS (FULL) OF 'DEPT' (TABLE) (Cost=22344 Card=10000000 Bytes=220000000)
ops$tkyte@ORA10G> set autotrace off
<b>so, it can flip flop the table it chooses to hash in 10g, whereas in 9i and before it would not</b>