When I get a tkprof output, I see "starts=1" data in the execution plan area, between the time and cost data. So what does that mean?
Basically, this area :
(cr=15 pr=0 pw=0 time=514 us starts=1 cost=3 size=7383 card=107)
********************************************************************************
SQL ID: 7jk33n4f4mpy9 Plan Hash: 1445457117
select *
from
hr.employees
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.04 0.03 0 351 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 9 0.00 0.00 0 15 0 107
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 11 0.04 0.03 0 366 0 107
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: SYS
Number of plan statistics captured: 1
Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
107 107 107 TABLE ACCESS FULL EMPLOYEES (cr=15 pr=0 pw=0 time=514 us starts=1 cost=3 size=7383 card=107)
********************************************************************************
STARTS is the number of times that line in the plan started. Easier to see when using (say) a join. Here's an example
SQL> select /*+ leading(d) use_nl(e) gather_plan_statistics */
2 e.ename, d.dname
3 from scott.dept d,
4 scott.emp e
5 where e.deptno = d.deptno
6 and e.sal > 1000;
ENAME DNAME
---------- --------------
CLARK ACCOUNTING
KING ACCOUNTING
MILLER ACCOUNTING
JONES RESEARCH
SCOTT RESEARCH
ADAMS RESEARCH
FORD RESEARCH
ALLEN SALES
WARD SALES
MARTIN SALES
BLAKE SALES
TURNER SALES
12 rows selected.
SQL>
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------
--------------------------------
SQL_ID 37nwzk5qypud3, child number 0
-------------------------------------
select /*+ leading(d) use_nl(e) gather_plan_statistics */
e.ename, d.dname from scott.dept d, scott.emp e where
e.deptno = d.deptno and e.sal > 1000
Plan hash value: 4192419542
-------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 12 |00:00:00.01 | 32 |
| 1 | NESTED LOOPS | | 1 | 13 | 12 |00:00:00.01 | 32 |
| 2 | TABLE ACCESS FULL| DEPT | 1 | 4 | 4 |00:00:00.01 | 7 |
|* 3 | TABLE ACCESS FULL| EMP | 4 | 3 | 12 |00:00:00.01 | 25 |
-------------------------------------------------------------------------------------
We scanned DEPT and got 4 rows. For each of those 4 rows, we then did a full scan of EMP, hence line 3 started 4 times.
Related
Hi I have the following table and I want to update the rows with same group where if the pervious row that have count zero have value I want to populate the other one with it if the row that needs to be populated was null otherwise no changes will be done.
Table 1 before update:
Group | Count | Name | lastName |
Grp1 | 0 | tyler | calark |
Grp1 | 1 | lara | jason |
Grp1 | 2 | null | spark |
Table 1 after update:
Group | Count | Name | lastName |
Grp1 | 0 | tyler | calark |
Grp1 | 1 | lara | jason |
Grp1 | 2 | tyler | spark |
This is how I understood it.
Before:
SQL> select * From test;
CGROUP CCOUNT NAME LAST_NAME
---------- ---------- ---------- ----------
D1 0 tyler clark
D1 1 jason lara
D1 2 spark
Update:
SQL> update test a set
2 a.name = (select b.name
3 from test b
4 where b.cgroup = a.cgroup
5 and b.ccount = 0
6 )
7 where a.name is null;
1 row updated.
After:
SQL> select * From test;
CGROUP CCOUNT NAME LAST_NAME
---------- ---------- ---------- ----------
D1 0 tyler clark
D1 1 jason lara
D1 2 tyler spark
SQL>
In this question - why adding order by in the query changes the aggregate value? - I was told that "If the window clause contains both PARTITION BY and ORDER BY, it returns the running count within the partition . So, using the ORDER BY expression, how many rows have been counted so far within the partition."
Referring to this example - https://www.vertica.com/docs/11.0.x/HTML/Content/Authoring/AnalyzingData/SQLAnalytics/ReportingAggregates.htm?tocpath=Analyzing%20Data%7CSQL%20Analytics%7CWindow%20Framing%7C_____3
Why does the cumulative count shows 4 (last value of count) for all values of sal=109?
=> SELECT deptno, sal, empno, COUNT(sal) OVER (
-> PARTITION BY deptno ORDER BY sal
-> ) AS COUNT
-> FROM emp;
deptno | sal | empno | count
--------+-----+-------+-------
10 | 101 | 1 | 1
10 | 104 | 4 | 2
------------------------------
20 | 100 | 11 | 1
20 | 109 | 7 | 4<-
20 | 109 | 6 | 4<-
20 | 109 | 8 | 4<-
20 | 110 | 10 | 6<-
20 | 110 | 9 | 6<-
------------------------------
30 | 102 | 2 | 1
30 | 103 | 3 | 2
30 | 105 | 5 | 3
You order by sal, which is at 109 for 3 rows within deptno 20. For the ordering criteria, there are 3 rows that should appear at the same time. After all 3 are added, after 100 for 1 row, you are immediately at 4. So you get 4 for all 3.
You need distinct ordering values to get distinct running count results.
The information of "table access full" appears in the SQL execution plan of a SQL statement.
I want to know why it does not use index.
Or is there any other optimizations?
After checking ,I get the info:
“table access full” appeared in hash join.
Columns of two tables in hash join have the same type.
DB version 10.2.0.3.
The column in "table access full" has index.
The last collection of tables and index statistics was dated June
12th.
When I run a single table queries on the column, the index is used.
Details:
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------
SQL_ID 5wz1z56666666, child number 13
--------------------------------------
SELECT count(Id) num FROM ccshare.blue WHERE id in (select id from(select
a.*,b.documentid,b.variablename,b.variablecname,b.variablevalue from ccshare.blue
a,ccshare.blueEx b where a.id= b.documentid and a.formid= :1 and
a.MdlId= :2 and (a.tflag= :3 or a.tflag is null) and (a.overflag =
:4 or a.overflag = 0) and (a.pages <> :5 and a.pages <> 9) and a.subject like :6)
cc group by id having count(id)>= 0) AND ((hisuserids like :7 or curuserids like :8 or
curuserids1 like :9 or curuserids2 like :10) and (hisdeluserids not like :11 or hisdeluserids
is null)) AND (tflag= :12 or tflag is null) AND formid> :13
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------
Plan hash value: 1599999999
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | 45356 (100)| |
| 1 | SORT AGGREGATE | | 1 | 344 | | | |
| 2 | NESTED LOOPS | | 1410 | 473K| | 45356 (2)| 00:09:05 |
| 3 | VIEW | VW_NSO_1 | 1410 | 18330 | | 43937 (2)| 00:08:48 |
|* 4 | FILTER | | | | | | |
| 5 | HASH GROUP BY | | 1410 | 103K| | 43937 (2)| 00:08:48 |
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------
|* 6 | HASH JOIN | | 405K| 29M| 2232K| 43903 (2)| 00:08:47 |
|* 7 | TABLE ACCESS FULL | blue | 28186 | 1899K| | 11964 (2)| 00:02:24 |
| 8 | INDEX FAST FULL SCAN | blueEX_DOCUID | 15M| 87M| | 18550 (2)| 00:03:43 |
|* 9 | TABLE ACCESS BY INDEX ROWID| blue | 1 | 331 | | 2 (0)| 00:00:01 |
|* 10 | INDEX UNIQUE SCAN | blue_KEY | 1 | | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$09D7319C
PLAN_TABLE_OUTPUT
---------------------------------------------------------------
3 - SEL$833EDA65 / VW_NSO_1#SEL$09D7319C
4 - SEL$833EDA65
7 - SEL$833EDA65 / a#SEL$3
8 - SEL$833EDA65 / b#SEL$3
9 - SEL$09D7319C / blue#SEL$1
10 - SEL$09D7319C / blue#SEL$1
Outline Data
-------------
/*+
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('10.2.0.3')
ALL_ROWS
OUTLINE_LEAF(#"SEL$833EDA65")
OUTLINE_LEAF(#"SEL$09D7319C")
UNNEST(#"SEL$335DD26A")
OUTLINE(#"SEL$335DD26A")
MERGE(#"SEL$3")
OUTLINE(#"SEL$833EDA65")
OUTLINE(#"SEL$1")
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------
OUTLINE(#"SEL$2")
OUTLINE(#"SEL$3")
NO_ACCESS(#"SEL$09D7319C" "VW_NSO_1"#"SEL$09D7319C")
INDEX_RS_ASC(#"SEL$09D7319C" "blue"#"SEL$1" ("blue"."ID"))
LEADING(#"SEL$09D7319C" "VW_NSO_1"#"SEL$09D7319C" "blue"#"SEL$1")
USE_NL(#"SEL$09D7319C" "blue"#"SEL$1")
FULL(#"SEL$833EDA65" "a"#"SEL$3")
INDEX_FFS(#"SEL$833EDA65" "b"#"SEL$3" ("blueEX"."DOCUMENTID"))
LEADING(#"SEL$833EDA65" "a"#"SEL$3" "b"#"SEL$3")
USE_HASH(#"SEL$833EDA65" "b"#"SEL$3")
END_OUTLINE_DATA
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------------
*/
Peeked Binds (identified by position):
--------------------------------------
1 - :1 (NUMBER): 107889
2 - :2 (NUMBER): 188
3 - :3 (VARCHAR2(30), CSID=852): (null)
4 - :4 (NUMBER): -1
5 - :5 (NUMBER): 7
6 - :6 (VARCHAR2(30), CSID=852): '%he has a pretty dog%'
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
7 - :7 (VARCHAR2(30), CSID=852): '%,4026722,%'
8 - :8 (VARCHAR2(30), CSID=852): '%,4026722*,%'
9 - :9 (VARCHAR2(30), CSID=852): '%,4026722*,%'
10 - :10 (VARCHAR2(30), CSID=852): '%,4026722*,%'
11 - :11 (VARCHAR2(30), CSID=852): '%,4026722,%'
12 - :12 (VARCHAR2(30), CSID=852): (null)
13 - :13 (NUMBER): 0
Predicate Information (identified by operation id):
---------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------
4 - filter(COUNT(*)>=0)
6 - access("a"."ID"="b"."DOCUMENTID")
7 - filter(("a"."SUBJECT" LIKE :6 AND "a"."FORMID"=:1 AND "a"."MDLID"=:2 AND
"a"."PAGES"<>:5 AND "a"."PAGES"<>9 AND INTERNAL_FUNCTION("a"."OVERFLAG") AND
("a"."tflag" IS NULL OR "a"."tflag"=:3)))
9 - filter((("HISUSERIDS" LIKE :7 OR "CURUSERIDS" LIKE :8 OR "CURUSERIDS1" LIKE :9 OR
"CURUSERIDS2" LIKE :10) AND "FORMID">:13 AND ("HISDELUSERIDS" IS NULL OR "HISDELUSERIDS" NOT
LIKE :11) AND ("tflag" IS NULL OR "tflag"=:12)))
10 - access("ID"="$nso_col_1")
Column Projection Information (identified by operation id):
PLAN_TABLE_OUTPUT
---------------------------------------------------------------
1 - (#keys=0) COUNT(*)[22]
3 - "$nso_col_1"[NUMBER,22]
4 - "a"."ID"[NUMBER,22]
5 - "a"."ID"[NUMBER,22], COUNT(*)[22]
6 - (#keys=1) "a"."ID"[NUMBER,22]
7 - "a"."ID"[NUMBER,22]
8 - "b"."DOCUMENTID"[NUMBER,22]
10 - "blue".ROWID[ROWID,10]
SQL> explain plan for select count(id) from ccshare.blue;
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 302********
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 890 (2)| 00:00:11 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| blue_OVERFLAG | 1155K| 890 (2)| 00:00:11 |
10046 event info (tkprof) of the SQL:
TKPROF: Release 10.2.0.3.0 - Production on Thu Jun 21 22:48:56 2018
Copyright (c) 1982, 2005, Oracle. All rights reserved.
Trace file: /opt/app/oracle/admin/testdb/udump/testdb1_ora_7668044.trc
Sort options: default
********************************************************************************
count = number of times OCI procedure was executed
cpu = cpu time in seconds executing
elapsed = elapsed time in seconds executing
disk = number of physical reads of buffers from disk
query = number of buffers gotten for consistent read
current = number of buffers gotten in current mode (usually for update)
rows = number of rows processed by the fetch or execute call
********************************************************************************
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.01 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 6.67 16.00 163 139223 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 6.68 16.02 163 139223 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: SYS
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=139223 pr=163 pw=0 time=16007522 us)
0 NESTED LOOPS (cr=139223 pr=163 pw=0 time=16007499 us)
2 VIEW VW_NSO_1 (cr=139215 pr=163 pw=0 time=16005404 us)
2 FILTER (cr=139215 pr=163 pw=0 time=16005400 us)
2 HASH GROUP BY (cr=139215 pr=163 pw=0 time=16005391 us)
32 HASH JOIN (cr=139215 pr=163 pw=0 time=15614187 us)
2 TABLE ACCESS FULL blue (cr=53972 pr=0 pw=0 time=1725549 us)
15511417 INDEX FAST FULL SCAN blueEX_DOCUID (cr=85243 pr=163 pw=0 time=15516352 us)(object id 60473)
0 TABLE ACCESS BY INDEX ROWID GREEN (cr=8 pr=0 pw=0 time=1964 us)
2 INDEX UNIQUE SCAN blue_KEY (cr=6 pr=0 pw=0 time=961 us)(object id 60454)
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
library cache lock 1 0.00 0.00
SQL*Net message to client 2 0.00 0.00
gc current block 2-way 1645 0.00 0.62
db file sequential read 23 0.00 0.01
db file parallel read 8 0.00 0.00
gc cr multi block request 50254 0.00 7.45
db file scattered read 29 0.00 0.02
SQL*Net message from client 2 13.69 13.69
********************************************************************************
OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.01 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 6.67 16.00 163 139223 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 6.68 16.02 163 139223 0 1
Misses in library cache during parse: 1
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 4 0.00 0.00
SQL*Net message from client 4 19.75 39.08
library cache lock 1 0.00 0.00
gc current block 2-way 1645 0.00 0.62
db file sequential read 23 0.00 0.01
db file parallel read 8 0.00 0.00
gc cr multi block request 50254 0.00 7.45
db file scattered read 29 0.00 0.02
OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 0 0.00 0.00 0 0 0 0
Execute 0 0.00 0.00 0 0 0 0
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 0 0.00 0.00 0 0 0 0
Misses in library cache during parse: 0
1 user SQL statements in session.
0 internal SQL statements in session.
1 SQL statements in session.
********************************************************************************
Trace file: /opt/app/oracle/admin/testdb/udump/testdb1_ora_7668044.trc
Trace file compatibility: 10.01.00
Sort options: default
1 session in tracefile.
1 user SQL statements in trace file.
0 internal SQL statements in trace file.
1 SQL statements in trace file.
1 unique SQL statements in trace file.
52007 lines in trace file.
16 elapsed seconds in trace file.
My query is like this:
select id, fn_calc(col)
from table_a
order by id
offset 1483800 rows fetch next 100 rows only;
Note that the paging offset is extremely high - table_a has about 1,500,000 rows.
The above query takes very long, but when fn_calc(col) is replaced by col, query speed is satisfactory - at least 5 times faster. But when the offset is 0 or 100, two queries are almost equally fast. Why this difference?
Possible reasons I can think of:
Oracle executes fn_calc() 1483900 times, although it is logically not neccessary. (It is enough to call it only 100 times)
The calling cost for a user function in a query is very high.
I'm using Oracle 12c on ExaData.
Any suggestion can help.
UPDATE:
When the above query is changed as follows:
select id, fn_calc(col)
from
(
select id, col
from table_a
order by id
offset 1483800 rows fetch next 100 rows only
)
order by id
The query speed is comparable to the case when fn_calc() is not called at all.
Why this?
UPDATE:
The execution plans are as follows: (Sorry, currently only I have is SQLDevelper, so I had to capture the result.)
The first query:
The second query (which uses subquery):
To down-voters and close-voters: Please specify your concerns about this question before you vote. I'll update my question accordingly. This question is about the real and serious problem to me. Please do not deprive the chance to get help.
Firstly, what is the purpose of the pagination query when you are not using ORDER BY clause. You are randomly fetching the rows, i.e. Oracle will internally apply ORDER BY NULL.
Secondly, since you have the function in the SELECT and not in the filter predicate, the explain plan should be same. The only extra time spent should be due to the function.
For example,
SQL> CREATE OR REPLACE FUNCTION f_char(
2 i_empno NUMBER)
3 RETURN VARCHAR2
4 AS
5 v_empno VARCHAR2(10);
6 BEGIN
7 v_empno := TO_CHAR(i_empno);
8 return v_empno;
9 END;
10 /
Function created.
Let's compare the explain plan:
SQL> set autot on explain
SQL>
SQL> SELECT f_char(empno) FROM emp
2 OFFSET 5 ROWS
3 FETCH NEXT 5 rows only;
F_CHAR(EMPNO)
--------------------------------------------------------------------------------
7698
7782
7788
7839
7844
Execution Plan
----------------------------------------------------------
Plan hash value: 3611411408
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 28210 | 4 (0)| 00:00:01 |
|* 1 | VIEW | | 14 | 28210 | 4 (0)| 00:00:01 |
|* 2 | WINDOW NOSORT STOPKEY| | 14 | 56 | 4 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | EMP | 14 | 56 | 4 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=CASE WHEN
(5>=0) THEN 5 ELSE 0 END +5 AND "from$_subquery$_002"."rowlimit_$$
_rownu
mber">5)
2 - filter(ROW_NUMBER() OVER ( ORDER BY NULL )<=CASE WHEN (5>=0)
THEN 5 ELSE 0 END +5)
SQL> SELECT empno FROM emp
2 OFFSET 5 ROWS
3 FETCH NEXT 5 rows only;
EMPNO
----------
7698
7782
7788
7839
7844
Execution Plan
----------------------------------------------------------
Plan hash value: 3611411408
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 364 | 4 (0)| 00:00:01 |
|* 1 | VIEW | | 14 | 364 | 4 (0)| 00:00:01 |
|* 2 | WINDOW NOSORT STOPKEY| | 14 | 56 | 4 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | EMP | 14 | 56 | 4 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=CASE WHEN
(5>=0) THEN 5 ELSE 0 END +5 AND "from$_subquery$_002"."rowlimit_$$
_rownu
mber">5)
2 - filter(ROW_NUMBER() OVER ( ORDER BY NULL )<=CASE WHEN (5>=0)
THEN 5 ELSE 0 END +5)
I used to think that Oracle does not index a row when one of the column values is null.
Some simple experimentation shows this to be not the case. I was able to run some queries unexpectedly accessing only indexes even though some columns were nullable (which of course was a pleasant surprise).
A Google search led to some blogs with conflicting answers: I have read that a row gets indexed unless all indexed columns are null, and also that a row gets indexed unless the leading column value for the index is null.
So, in what cases does a row not enter an index? Is this Oracle version specific?
If any indexed column contains a non-null value that row will be indexed. As you can see in the following example only one row doesn't get indexed and that's the row which has NULL in both indexed columns. You can also see that Oracle definitely does index a row when the leading index column has a NULL value.
SQL> create table big_table as
2 select object_id as pk_col
3 , object_name as col_1
4 , object_name as col_2
5 from all_objects
6 /
Table created.
SQL> select count(*) from big_table
2 /
COUNT(*)
----------
69238
SQL> insert into big_table values (9999990, null, null)
2 /
1 row created.
SQL> insert into big_table values (9999991, 'NEW COL 1', null)
2 /
1 row created.
SQL> insert into big_table values (9999992, null, 'NEW COL 2')
2 /
1 row created.
SQL> select count(*) from big_table
2 /
COUNT(*)
----------
69241
SQL> create index big_i on big_table(col_1, col_2)
2 /
Index created.
SQL> exec dbms_stats.gather_table_stats(user, 'BIG_TABLE', cascade=>TRUE)
PL/SQL procedure successfully completed.
SQL> select num_rows from user_indexes where index_name = 'BIG_I'
2 /
NUM_ROWS
----------
69240
SQL> set autotrace traceonly exp
SQL>
SQL> select pk_col from big_table
2 where col_1 = 'NEW COL 1'
3 /
Execution Plan
----------------------------------------------------------
Plan hash value: 1387873879
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 60 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_TABLE | 2 | 60 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | BIG_I | 2 | | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("COL_1"='NEW COL 1')
SQL> select pk_col from big_table
2 where col_2 = 'NEW COL 2'
3 /
Execution Plan
----------------------------------------------------------
Plan hash value: 3993303771
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 60 | 176 (1)| 00:00:03 |
|* 1 | TABLE ACCESS FULL| BIG_TABLE | 2 | 60 | 176 (1)| 00:00:03 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("COL_2"='NEW COL 2')
SQL> select pk_col from big_table
2 where col_1 is null
3 and col_2 = 'NEW COL 2'
4 /
Execution Plan
----------------------------------------------------------
Plan hash value: 1387873879
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 53 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_TABLE | 1 | 53 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | BIG_I | 2 | | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("COL_1" IS NULL AND "COL_2"='NEW COL 2')
filter("COL_2"='NEW COL 2')
SQL> select pk_col from big_table
2 where col_1 is null
3 and col_2 is null
4 /
Execution Plan
----------------------------------------------------------
Plan hash value: 3993303771
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 53 | 176 (1)| 00:00:03 |
|* 1 | TABLE ACCESS FULL| BIG_TABLE | 1 | 53 | 176 (1)| 00:00:03 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("COL_1" IS NULL AND "COL_2" IS NULL)
SQL>
This example run on Oracle 11.1.0.6. But I'm pretty confident it holds true for all versions.
And in addition to APC's answer: when you want to index a NULL value, you can add a constant expression to the index.
Example:
SQL> select * from v$version where rownum = 1
2 /
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
1 rij is geselecteerd.
SQL> create table t (id,status,fill)
2 as
3 select level
4 , nullif(ceil((level-1)/1000),0)
5 , lpad('*',1000,'*')
6 from dual
7 connect by level <= 10000
8 /
Tabel is aangemaakt.
SQL> select status
2 , count(*)
3 from t
4 group by status
5 /
STATUS COUNT(*)
---------- ----------
1 1000
2 1000
3 1000
4 1000
5 1000
6 1000
7 1000
8 1000
9 1000
10 999
1
11 rijen zijn geselecteerd.
SQL> create index i_status on t(status)
2 /
Index is aangemaakt.
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true)
PL/SQL-procedure is geslaagd.
SQL> set autotrace traceonly
SQL> select *
2 from t
3 where status is null
4 /
1 rij is geselecteerd.
Uitvoeringspan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=201 Card=1 Bytes=1007)
1 0 TABLE ACCESS (FULL) OF 'T' (TABLE) (Cost=201 Card=1 Bytes=1007)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
364 consistent gets
0 physical reads
0 redo size
1265 bytes sent via SQL*Net to client
242 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
Please note the full table scan and the 364 consistent gets.
SQL> set autotrace off
SQL> create index i_status2 on t(status,1)
2 /
Index is aangemaakt.
SQL> set autotrace traceonly
SQL> select *
2 from t
3 where status is null
4 /
1 rij is geselecteerd.
Uitvoeringspan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=1 Card=1 Bytes=1007)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (TABLE) (Cost=1 Card=1 Bytes=1007)
2 1 INDEX (RANGE SCAN) OF 'I_STATUS2' (INDEX) (Cost=1 Card=1)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
3 consistent gets
1 physical reads
0 redo size
1265 bytes sent via SQL*Net to client
242 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
And now it uses the index and has only 3 consistent gets.
Regards,
Rob.
In addition to APC's answer, NULLS are indexed in bitmap indexes.