Oracle FAQ Your Portal to the Oracle Knowledge Grid
HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US
 

Home -> Community -> Usenet -> c.d.o.misc -> Re: how to find "bottom" of table in a group query

Re: how to find "bottom" of table in a group query

From: Christopher Beck <clbeck_at_us.oracle.com>
Date: Wed, 03 Mar 1999 20:58:46 GMT
Message-ID: <36dd950a.32148016@inet16.us.oracle.com>


On Wed, 03 Mar 1999 14:10:17 +0200, Werner <werner.smit_at_face.co.zackityspam> wrote:

>Consider table region_log:
> Name Null? Type
> ------------------------------- -------- ----
> AREA_CDE NOT NULL CHAR(4)
> TIMESTAMP NOT NULL DATE
> MODULE NOT NULL VARCHAR2(48)
> STAFF_ID_NO CHAR(13)
> ACTION VARCHAR2(48)
> REMARKS VARCHAR2(256)
>
>
>This is a log file.
>I use a select statement to see what is going on today by saying...
> select *
> from region_log
> where timestamp > sysdate-1
> order by area_cde, staff_id_no, timestamp
>
>But I would very much like to only see the last 10 transactions done by
>each staff_if_no.
>
>I thought there should be some easy way to do this. Bust everytime I
>attempt
>it the SQL statement grow so big that I give up on it.
>
>I was hoping for something like:
>select *
> from region_log r1
> where r1.timestamp = (Select max(timestamp)
> from region_log r2
> where timestamp > sysdate-1
> group by staff_id_no)
>
>But this cause error 1427:Single row subquery returns more than one
>row..
>Which I can understand.
>Is there an easy way of finding the last entry for each staff_id_no?
>
>And after finding the last entry , finding the 9 before that ?

You can do it with a index, a hint and a table with the staff_ids.

Consider...

SQL> create table emp as select * from scott.emp; Table created.

SQL> create table dept as select * from scott.dept; Table created.

SQL> select ename, deptno, hiredate
  2 from emp
  3 order by deptno, hiredate desc
  4 /

ENAME DEPTNO HIREDATE

---------- ---------- ---------
MILLER             10 23-JAN-82
KING               10 17-NOV-81
CLARK              10 09-JUN-81
ADAMS              20 12-JAN-83
SCOTT              20 09-DEC-82
FORD               20 03-DEC-81
JONES              20 02-APR-81
SMITH              20 17-DEC-80
JAMES              30 03-DEC-81
MARTIN             30 28-SEP-81
TURNER             30 08-SEP-81
BLAKE              30 01-MAY-81
WARD               30 22-FEB-81
ALLEN              30 20-FEB-81


SQL> select * from dept;

    DEPTNO DNAME LOC
---------- -------------- -------------

        10 ACCOUNTING     NEW YORK
        20 RESEARCH       DALLAS
        30 SALES          CHICAGO
        40 OPERATIONS     BOSTON


First we want the last 5 people hired so we can first

SQL> create index hiredate_idx on emp( hiredate ); Index created.

And now if we hint the query with INDEX_DESC we get

SQL> select /*+ INDEX_DESC( emp hiredate_idx ) */   2 ename, to_char( hiredate, 'dd-mon-yyyy hh24:mi:ss' )   3 from emp
  4 where hiredate > to_date( '01-JAN-0001', 'dd-mon-yyyy' )   5 and rownum < 6
  6 /

ENAME TO_CHAR(HIREDATE,'DD

---------- --------------------
ADAMS      12-jan-1983 00:00:00
SCOTT      09-dec-1982 00:00:00
MILLER     23-jan-1982 00:00:00
FORD       03-dec-1981 00:00:00
JAMES      03-dec-1981 00:00:00


Execution Plan


   0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=5 Bytes=125)    1 0 COUNT (STOPKEY)

   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (Cost=1 Card=5 Bytes=125)
   3    2       INDEX (RANGE SCAN DESCENDING) OF 'HIREDATE_IDX' (NON-UNIQUE)
(Cost=2 Card=5)

You can see that we do a index range scan descending on the hiredate_idx. You will also see that I put 'where hiredate > some real old date'. That is so the SQL parser will use our index and hint. Without it we would get

SQL> select /*+ INDEX_DESC( emp hiredate_idx ) */   2 ename, to_char( hiredate, 'dd-mon-yyyy hh24:mi:ss' )   3 from emp
  4 where rownum < 6
  5 /

ENAME TO_CHAR(HIREDATE,'DD

---------- --------------------
SMITH      17-dec-1980 00:00:00
ALLEN      20-feb-1981 00:00:00
WARD       22-feb-1981 00:00:00
JONES      02-apr-1981 00:00:00
MARTIN     28-sep-1981 00:00:00


Execution Plan


   0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=82 Bytes=1312)    1 0 COUNT (STOPKEY)
   2 1 TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=82 Bytes=1312)

You can include other constraints also. Say you want the last two people hired in dept 10.

SQL> select /*+ INDEX_DESC( emp hiredate_idx ) */   2 ename, to_char( hiredate, 'dd-mon-yyyy hh24:mi:ss' )   3 from emp
  4 where hiredate > to_date( '01-JAN-0001', 'dd-mon-yyyy' )

  5     and deptno = 10
  6     and rownum < 3

  7 /

ENAME TO_CHAR(HIREDATE,'DD

---------- --------------------
MILLER     23-jan-1982 00:00:00
KING       17-nov-1981 00:00:00


So now we can ask for the last 2 people hired in each dept.

SQL> break on deptno
SQL> select e.deptno, e.ename, to_char( hiredate, 'dd-mon-yyyy hh24:mi:ss' )   2 from emp e, dept d
  3 where e.empno in ( select /*+ INDEX_DESC( emp hiredate_idx ) */ empno

  4                       from emp
  5                      where hiredate > to_date( '01-JAN-0001', 'dd-mon-yyyy')
  6                        and rownum < 3
  7                        and deptno = d.deptno )
  8     and e.deptno = d.deptno

  9 /

    DEPTNO ENAME TO_CHAR(HIREDATE,'DD

---------- ---------- --------------------
        10 MILLER     23-jan-1982 00:00:00
           KING       17-nov-1981 00:00:00
        20 ADAMS      12-jan-1983 00:00:00
           SCOTT      09-dec-1982 00:00:00
        30 JAMES      03-dec-1981 00:00:00
           MARTIN     28-sep-1981 00:00:00

6 rows selected.

hope this helps.

chris.

>
>Thanks
>Werner
>
>PS:Sorry aboyt the subject heading : couldn't think of anything better.

--
Christopher Beck
Oracle Corporation
clbeck_at_us.oracle.com
Reston, VA.



Opinions are mine and do not necessarily reflect those of Oracle Corporation Received on Wed Mar 03 1999 - 14:58:46 CST

Original text of this message

HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US