No, it doesn't, we do employ short circuit evaluation.
However, you have no control, none, over the order of evaluation.
I'd be really interested how you can tell from a PLAN that it continues?
Please do not refer to that site, first and foremost, what is written there does NOT prove your point, nor does it even *support* your point. No where in there does it show that the entire predicate is evaluated - it only shows that in fact THE PREDICATE IS IN FACT evaluated. The page referenced does beg the question "why would one fetch from a query if the only goal was to get the column headings???". First, they would just prepare the query - get the column headings - and wouldn't want to or need to fetch from it ever - the full scan would NEVER happen.
Here is a "proof" that we short circuit:
ops$tkyte%ORA11GR2> create table t ( x varchar2(11) );
Table created.
ops$tkyte%ORA11GR2>
ops$tkyte%ORA11GR2> insert into t values ('hello world');
1 row created.
ops$tkyte%ORA11GR2>
ops$tkyte%ORA11GR2> select * from t where 1=0 and to_number(x) > 0;
no rows selected
ops$tkyte%ORA11GR2> select * from t where 1=1 and to_number(x) > 0;
select * from t where 1=1 and to_number(x) > 0
*
ERROR at line 1:
ORA-01722: invalid number
the first query would have failed. Here is further evidence that we short circuit and don't even EVALUATE a query if we know the predicate is false:
ops$tkyte%ORA11GR2> /*
ops$tkyte%ORA11GR2> drop table t;
ops$tkyte%ORA11GR2>
ops$tkyte%ORA11GR2> create table t as select * from all_objects;
ops$tkyte%ORA11GR2> */
ops$tkyte%ORA11GR2>
ops$tkyte%ORA11GR2> variable var number
ops$tkyte%ORA11GR2>
ops$tkyte%ORA11GR2> set autotrace traceonly explain
ops$tkyte%ORA11GR2> select * from t where object_id = 42 and exists (select null from t t2 where t2.object_id = t.object_id) and :var = 1
2 union all
3 select * from t where object_id = 43 and exists (select null from t t2 where t2.object_id = t.object_id) and :var = 2
4 /
Execution Plan
----------------------------------------------------------
Plan hash value: 2603986468
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 184 | 865 (1)| 00:00:11 |
|* 1 | HASH JOIN SEMI | | 1 | 184 | 865 (1)| 00:00:11 |
| 2 | VIEW | VW_JF_SET$5D76B708 | 24 | 4104 | 576 (1)| 00:00:07 |
| 3 | UNION-ALL | | | | | |
|* 4 | FILTER | | | | | |
|* 5 | TABLE ACCESS FULL| T | 12 | 1896 | 288 (1)| 00:00:04 |
|* 6 | FILTER | | | | | |
|* 7 | TABLE ACCESS FULL| T | 12 | 1896 | 288 (1)| 00:00:04 |
| 8 | TABLE ACCESS FULL | T | 65315 | 829K| 288 (1)| 00:00:04 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T2"."OBJECT_ID"="ITEM_1")
4 - filter(TO_NUMBER(:VAR)=1)
5 - filter("OBJECT_ID"=42)
6 - filter(TO_NUMBER(:VAR)=2)
7 - filter("OBJECT_ID"=43)
Note
-----
- dynamic sampling used for this statement (level=2)
<b>See those filters? they cause a pruning to happen - dynamically - at runtime. We can see this when we run the query:</b>
ops$tkyte%ORA11GR2> set autotrace off
ops$tkyte%ORA11GR2>
ops$tkyte%ORA11GR2>
ops$tkyte%ORA11GR2> exec :var := 1
PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> @trace
ops$tkyte%ORA11GR2> alter session set events '10046 trace name context forever, level 12';
Session altered.
ops$tkyte%ORA11GR2> select * from t where object_id = 42 and exists (select null from t t2 where t2.object_id = t.object_id) and :var = 1
2 union all
3 select * from t where object_id = 43 and exists (select null from t t2 where t2.object_id = t.object_id) and :var = 2
4 /
OWNER OBJECT_NAME SUBOBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE CREATED LAST_DDL_ TIMESTAMP STATUS T G S NAMESPACE EDITION_NAME
------------------------------ ------------------------------ ------------------------------ ---------- -------------- ------------------- --------- --------- ------------------- ------- - - - ---------- ------------------------------
SYS
<b>In the tkprof you will find something like this:</b>
select * from t where object_id = 42 and exists (select null from t t2 where t2.object_id = t.object_id) and :var = 1
union all
select * from t where object_id = 43 and exists (select null from t t2 where t2.object_id = t.object_id) and :var = 2
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.01 0.01 0 10 0 0
Fetch 2 0.05 0.05 761 1037 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.06 0.07 761 1047 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 243
Number of plan statistics captured: 1
Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
1 1 1 HASH JOIN SEMI (cr=1037 pr=761 pw=0 time=54707 us cost=865 size=184 card=1)
1 1 1 VIEW VW_JF_SET$5D76B708 (cr=1033 pr=761 pw=0 time=54362 us cost=576 size=4104 card=24)
1 1 1 UNION-ALL (cr=1033 pr=761 pw=0 time=54357 us)
1 1 1 FILTER (cr=1033 pr=761 pw=0 time=54336 us)
1 1 1 TABLE ACCESS FULL T (cr=1033 pr=761 pw=0 time=54328 us cost=288 size=1896 card=12)
0 0 0 FILTER (cr=0 pr=0 pw=0 time=3 us)
0 0 0 TABLE ACCESS FULL T (cr=0 pr=0 pw=0 time=0 us cost=288 size=1896 card=12)
53 53 53 TABLE ACCESS FULL T (cr=4 pr=0 pw=0 time=92 us cost=288 size=849095 card=65315)
See the "TABLE ACCESS FULL T (cr=0 pr=0 ..." bit? We DID NOT READ THAT TABLE, not at all.
and - you would see the SAME EXACT THING in 9i, 10g - 11g.
The referenced article is actually wrong on many levels. They actually proved themselves wrong but don't seem to know it. They wrote:
It is this second case, “WHERE 1=2” I want to talk about. Of course since 1 never equals 2 you never get a result back, however it forces a full table scan! In testing with 10gR1 version 10.1.0.4 and on 9iR2, 9.2.0.4 then later they "show" :
SQL> select count(object_id) from test where 1=2;
COUNT(OBJECT_ID)
----------------
0
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=23 Card=1 Bytes=4)
1 0 SORT (AGGREGATE)
2 1 FILTER
3 2 INDEX (FAST FULL SCAN) OF 'TEST_IDX' (INDEX) (Cost=23
Card=50115 Bytes=200460)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
0 consistent gets
0 physical reads
0 redo size
310 bytes sent via SQL*Net to client
496 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed which SHOWS that the database didn't full scan. And they go on to show that over and over.
Then they erroneously claim:
In actuality it seems to be another case of Oracle explain plans not actually matching what is happening in the database.But - do you see that filter step in the plan??? That filter step is 1=2, we KNOW we don't have to do the subtree, we don't do the subtree. Where is the plan wrong? It shows what is happening, the autotrace shows it is happening (or not happening actually)
What is funny though, is this:
What we ended up doing in this clients case was to replace the “WHERE 1=2” with a clause that equated the primary key of the table with an impossible value for that key, in this case the ID was being passed in as a GUID (a hexadecimal value) so we use a “WHERE KEY=HEX(00)” and got a low cost unique index lookup instead of a costly full table scan.They replaced "nothing" with "something"
They took a query that DID NOTHING, NO WORK AT ALL
and replaced it with a query that had to do work.
Think about that for a minute.
Who cares about the cost of a query? I care about the amount of work done by said query. I'd prefer a query that does NOTHING versus a query that does SOMETHING - if I was just getting column headings....