You Asked
Hello Tom
I have a small question. I am trying to make query statement return the last know insert, faster than I currently do, using the analytic function
I have a table:
here is an example
create table t ( a int, b date, c number );
Values are inserted from a program every day.
insert into t values ( 1, to_date( '01-jan-2004'), 1 );
insert into t values ( 2, to_date( '02-jan-2004'), 2 );
insert into t values ( 1, to_date( '03-jan-2004'), 3 );
insert into t values ( 2, to_date( '04-jan-2004'), 5 );
insert into t values ( 1, to_date( '05-jan-2004'), 4 );
insert into t values ( 2, to_date( '06-jan-2004'), 3 );
insert into t values ( 1, to_date( '07-jan-2004'), 2 );
insert into t values ( 2, to_date( '08-jan-2004'), 6 );
insert into t values ( 1, to_date( '09-jan-2004'), 7 );
insert into t values ( 2, to_date( '10-jan-2004'), 9 );
to get the last entry inserted I would normaly do
select * from
(select a,b,c from t order by b desc)
where rownum <2
As the table grows the query takes longer and longer.
My question is whether I can make the query faster by using analytic functions? My current query works but does not seem very efficient.
I have tried LAST_VALUE as follows but I always get more than what I expect.
select a,b,last_value(c)
over (partition by a order by b)
from t
where a=2
What would be the equivalent query using analytic functions?
Thanks for any advice.
and Tom said...
that query should perform as fast for a 1 row table as it does for a 1,000,000,000,000,000,000 row table.
first_rows hint might be called for and an index on B would be the thing that makes this just as fast for 1 row as for <as many as you can find> rows.
B should be NOT NULL, B should be indexed, then this will be super fast:
ops$tkyte@ORA9IR2> create table t ( a int, b date NOT NULL, c number );
Table created.
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> create index t_idx on t(b);
Index created.
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> insert into t select rownum, sysdate+rownum, user_id from all_users;
46 rows created.
ops$tkyte@ORA9IR2> analyze table t compute statistics for table;
Table analyzed.
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> set autotrace on
ops$tkyte@ORA9IR2> select * from
2 ( select /*+ FIRST_ROWS */ a, b, c from t order by b desc )
3 where rownum < 2;
A B C
---------- --------- ----------
46 27-SEP-04 195
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=5 Card=1 Bytes=35)
1 0 COUNT (STOPKEY)
2 1 VIEW (Cost=5 Card=46 Bytes=1610)
3 2 SORT (ORDER BY STOPKEY) (Cost=5 Card=46 Bytes=782)
4 3 TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=46 Bytes=782)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
482 bytes sent via SQL*Net to client
499 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1 rows processed
ops$tkyte@ORA9IR2> set autotrace off
ops$tkyte@ORA9IR2> truncate table t;
Table truncated.
ops$tkyte@ORA9IR2> insert into t select rownum, sysdate+rownum, object_id from all_objects;
30990 rows created.
ops$tkyte@ORA9IR2> analyze table t compute statistics for table;
Table analyzed.
ops$tkyte@ORA9IR2> set autotrace on
ops$tkyte@ORA9IR2> select * from
2 ( select /*+ FIRST_ROWS */ a, b, c from t order by b desc )
3 where rownum < 2;
A B C
---------- --------- ----------
30990 17-JUN-89 129013
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=826 Card=1 Bytes=35)
1 0 COUNT (STOPKEY)
2 1 VIEW (Cost=826 Card=30990 Bytes=1084650)
3 2 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=826 Card=30990 Bytes=619800)
4 3 INDEX (FULL SCAN DESCENDING) OF 'T_IDX' (NON-UNIQUE) (Cost=26 Card=30990)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
485 bytes sent via SQL*Net to client
499 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
ops$tkyte@ORA9IR2> set autotrace off
in this case, analytics can only be used to slow down the query! rownum is by far the most efficient solution here.
Even if you cannot index, this is true. there is special top-n processing that takes place with rownum that cannot with analytics.
rownum is right, analytics are wrong, you are probably either missing the NOT NULL on B (meaning that the index cannot be used) or the index on B
Rating
(1 rating)
Is this answer out of date? If it is, please let us know via a Comment