This will read your table once.
The join is done with an auxiliary table which contains only two values(of course this in in memory - no I/O)
with t as(
select '1' a, '4' b, '7' c from dual union all
select '2', '5', '5' from dual union all
select '3', '6', '8' from dual
)
select decode(aux.col,1,t.a,'R'), t.b, t.c
from t
join (select '1' col from dual union all select '2' from dual) aux
on (aux.col='1' or t.b=t.c);
The query does not depend on '1' and '2'. It can be:
select decode(aux.col, 'bla', t.a,'R'), t.b, t.c
from t
join (select 'bla' col from dual union all select 'otherbla' from dual) aux
on (aux.col='bla' or t.b=t.c);
UPDATE: Also, if number of b=c records is small, you can speed up your actual query creating an index:
create index fbi on t (b-c);
and then replace in your query WHERE b = c
with WHERE b - c = 0
UPDATE2 Just to get the ideea on how these queries are executed:
create table t(a varchar2(10), b varchar2(10), c varchar2(10));
insert into t
select mod(dbms_random.random(),1000),
mod(dbms_random.random(),1000),
mod(dbms_random.random(),1000)
from dual
connect by level < 1000000;
exec DBMS_STATS.GATHER_TABLE_STATS('DEV','T');
--1
SELECT a, b, c
FROM t;
---------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------
| 0 | SELECT STATEMENT | | 999K| 11M| 700 (3)|
| 1 | TABLE ACCESS FULL| T | 999K| 11M| 700 (3)|
---------------------------------------------------------------
--2
SELECT a, b, c
FROM t
union all
SELECT 'R',b,c
FROM t
WHERE b = c;
----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1009K| 11M| 1426 (53)|
| 1 | UNION-ALL | | | | |
| 2 | TABLE ACCESS FULL| T | 999K| 11M| 700 (3)|
| 3 | TABLE ACCESS FULL| T | 10000 | 97K| 726 (7)|
----------------------------------------------------------------
--3
select decode(aux.col, 'bla', t.a,'R'), t.b, t.c
from t
join (select 'bla' col from dual union all select 'otherbla' from dual) aux
on (aux.col='bla' or t.b=t.c);
----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20990 | 368K| 1402 (3)|
| 1 | NESTED LOOPS | | 20990 | 368K| 1402 (3)|
| 2 | VIEW | | 2 | 12 | 4 (0)|
| 3 | UNION-ALL | | | | |
| 4 | FAST DUAL | | 1 | | 2 (0)|
| 5 | FAST DUAL | | 1 | | 2 (0)|
| 6 | TABLE ACCESS FULL| T | 10495 | 122K| 699 (3)|
----------------------------------------------------------------
--if the leading table is dual, can be used an /*+ordered*/ hint
--after select clause
--4
create index fbi on t (b-c);
SELECT a, b, c
FROM t
union all
SELECT 'R',b,c
FROM t
WHERE b - c = 0;
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1009K| 11M| 1384 (51)|
| 1 | UNION-ALL | | | | |
| 2 | TABLE ACCESS FULL | T | 999K| 11M| 700 (3)|
| 3 | TABLE ACCESS BY INDEX ROWID| T | 10000 | 117K| 683 (1)|
| 4 | INDEX RANGE SCAN | FBI | 4000 | | 3 (0)|
--------------------------------------------------------------------------
Keep in mind that Oracle does not know to predict well on or join clauses like in 3, so better you force a desired execution path. You should test to choose between 2 and 3 and 4(with the back of the cost of the index).