SlideShare a Scribd company logo
Some SQL Techniques
Who am I

           •  Been with Oracle since
              1993
           •  User of Oracle since 1987
           •  The “Tom” behind AskTom
              in Oracle Magazine
               www.oracle.com/oramag
           •  Expert Oracle Database
              Architecture
           •  Effective Oracle by Design
           •  Expert One on One Oracle
           •  Beginning Oracle
Agenda

•  What do you need to write “good” SQL
•  The Schema Matters
•  Knowing what is available
    –    Using rownum (yes, to 'tune')
    –    Scalar subqueries
    –    Analytics
    –    Some hints
•  Don’t tune queries!
•  Other things
    –    Materialized Views
    –    With subquery factoring
    –    Merge
    –    …
What do you need to know…

•  Access Paths
     –    There are a lot of them
     –    There is no best one (else there would be, well, one)
•  A little bit of physics
     –    Full scans are not evil
     –    Indexes are not all goodness
•  How the data is managed by Oracle
     –    high water marks for example
     –    IOT’s, clusters, etc
•  What your query needs to actually do
     –    Is that outer join really necessary or “just in case”
Structures

   •  How the data is accessed and organized
      makes a difference
           –    Clustering



Select *
from orders o, line_items li
                               ORDERS ORDERS & LINE
where o.order# = li.order#           LINE ITEMS
                                              ITEMS
And o.order# = :order
Structures

     •  How the data is accessed and organized
        makes a difference
          –    Clustering
          –    Index Organized Tables


Select avg(price)                       STOCKS
                                        STOCKS
From stocks
Where symbol = ‘ORCL’
And stock_dt >= sysdate-5;
Structures

   •  How the data is accessed and organized
      makes a difference
      –    Clustering
      –    Index Organized Tables       ORDERS
                                       ORDERS
                                         ORDERS
      –    Partitioning                              Europe

                                                     USA
                                     Jan
                                      Jan Feb Feb
                                    Composite Partition
                                    Partition
                                    Large Table
                                    Higher and Manage
                                    Divide Performance
                                    Difficult to Conquer
                                    More flexibility to match
                                    Easier to Manage
                                    business needs
                                    Improve Performance
The Schema Matters

•  A Lot!
•  Tune this query:
    Select DOCUMENT_NAME, META_DATA
      from documents
     where userid=:x;
•  That is about as easy as it gets (the SQL)
•  Not too much we can do to rewrite it…
•  But we’d like to make it better.
                                                Iot01.sql
                                                  Cf.sql
Organization Counts

ops$tkyte%ORA11GR2> create table iot
  2 ( username          varchar2(30),
  3    document_name    varchar2(30),
  4    other_data       char(1000),
  5    constraint iot_pk primary key (username,document_name))
  6 organization index
  7 /
Table created.

ops$tkyte%ORA11GR2> create table heap
  2 ( username          varchar2(30),
  3    document_name    varchar2(30),
  4    other_data       char(1000),
  5    constraint heap_pk primary key (username,document_name))
  6 /
Table created.
Organization Counts
ops$tkyte%ORA11GR2> begin
  2      for i in 1 .. 100
  3      loop
  4           for x in ( select username from all_users )
  5           loop
  6                insert into heap
  7                (username,document_name,other_data) values
  8                ( x.username, x.username || '_' || i, 'x' );
  9
 10                insert into iot
 11                (username,document_name,other_data) values
 12                ( x.username, x.username || '_' || i, 'x' );
 13           end loop;
 14      end loop;
 15      dbms_stats.gather_table_stats
 16      ( user, 'IOT', cascade=>true );
 17      dbms_stats.gather_table_stats
 18      ( user, 'HEAP', method_opt=>'for all indexed columns',
                           cascade=>true );
 19      commit;
 20 end;
 21 /
Organization Counts
ops$tkyte%ORA11GR2> declare
  2      l_rec    heap%rowtype;
  3      cursor heap_cursor(p_username in varchar2) is
  4      select * from heap single_row where username = p_username;
  5      cursor iot_cursor(p_username in varchar2) is
  6      select * from iot single_row where username = p_username;
  7 begin
  8 for i in 1 .. 10
  9 loop
 10      for x in (select username from all_users) loop
 11          open heap_cursor(x.username);
 12          loop
 13               fetch heap_cursor into l_rec;
 14               exit when heap_cursor%notfound;
 15          end loop;
 16          close heap_cursor;
 17          open iot_cursor(x.username);
 18          loop
 19               fetch iot_cursor into l_rec;
 20               exit when iot_cursor%notfound;
 21          end loop;
 22          close iot_cursor;
 23      end loop;
 24 end loop;
 25 end;
 26 /
Organization Counts
   ops$tkyte%ORA11GR2> declare
     2      type array is table of iot%rowtype;
     3      l_data array;
     4 begin
     5 for i in 1 .. 10
     6 loop
     7      for x in (select username from all_users)
     8      loop
     9           select * bulk collect into l_data
    10             from heap bulk_collect
    11            where username = x.username;
    12           select * bulk collect into l_data
    13             from iot bulk_collect
    14            where username = x.username;
    15      end loop;
    16 end loop;
    17 end;
    18 /
   PL/SQL procedure successfully completed.
Organization Counts

SELECT * FROM HEAP SINGLE_ROW WHERE USERNAME = :B1

call     count        cpu    elapsed       disk      query    current         rows
------- ------   -------- ---------- ---------- ---------- ----------   ----------
Parse        1       0.00       0.00          0          0          0            0
Execute    410       0.02       0.02          0          0          0            0
Fetch    41410       0.25       0.27          0      82810          0        41000
------- ------   -------- ---------- ---------- ---------- ----------   ----------
total    41821       0.28       0.30          0      82810          0        41000

Row Source Operation
---------------------------------------------------
TABLE ACCESS BY INDEX ROWID HEAP (cr=202 pr=0 pw=0 time=41 us cost=102 ...)
 INDEX RANGE SCAN HEAP_PK (cr=102 pr=0 pw=0 time=221 us cost=2 size=0 ca...)
Organization Counts

SELECT * FROM IOT SINGLE_ROW WHERE USERNAME = :B1

call     count        cpu    elapsed       disk      query    current         rows
------- ------   -------- ---------- ---------- ---------- ----------   ----------
Parse        1       0.00       0.00          0          0          0            0
Execute    410       0.02       0.02          0          0          0            0
Fetch    41410       0.16       0.18          0      42220          0        41000
------- ------   -------- ---------- ---------- ---------- ----------   ----------
total    41821       0.19       0.21          0      42220          0        41000

Row Source Operation
---------------------------------------------------
INDEX RANGE SCAN IOT_PK (cr=103 pr=0 pw=0 time=33 us cost=21 size=102000 ...)
Organization Counts

SELECT * FROM HEAP BULK_COLLECT WHERE USERNAME = :B1

call     count        cpu    elapsed       disk      query    current         rows
------- ------   -------- ---------- ---------- ---------- ----------   ----------
Parse        1       0.00       0.00          0          0          0            0
Execute    410       0.01       0.02          0          0          0            0
Fetch      410       0.11       0.12          0      42010          0        41000
------- ------   -------- ---------- ---------- ---------- ----------   ----------
total      821       0.13       0.14          0      42010          0        41000

Row Source Operation
---------------------------------------------------
TABLE ACCESS BY INDEX ROWID HEAP (cr=102 pr=0 pw=0 time=533 us cost=102 ...)
 INDEX RANGE SCAN HEAP_PK (cr=2 pr=0 pw=0 time=23 us cost=2 size=0 ca...)
Organization Counts

SELECT * FROM IOT BULK_COLLECT WHERE USERNAME = :B1

call     count        cpu    elapsed       disk      query    current         rows
------- ------   -------- ---------- ---------- ---------- ----------   ----------
Parse        1       0.00       0.00          0          0          0            0
Execute    410       0.01       0.02          0          0          0            0
Fetch      410       0.06       0.06          0       9000          0        41000
------- ------   -------- ---------- ---------- ---------- ----------   ----------
total      821       0.08       0.08          0       9000          0        41000

Row Source Operation
---------------------------------------------------
INDEX RANGE SCAN IOT_PK (cr=22 pr=0 pw=0 time=142 us cost=21 size=102000...)
Knowing what is available

•  There is a lot out there…
•  I learn something new every day
•  Skimming the docs works
   –    Oh, I remember something similar…
•  Check out the “whats new in” at the head of the
   docs
•  Participate in the forums
•  Things change… Some things must be
   “discovered”
                                          Ignulls.sql
You have to learn new things…

ops$tkyte%ORA11GR2> select dt, val
  2    from t
  3   order by dt;

DT               VAL
--------- ----------
02-JAN-11        195
03-JAN-11
04-JAN-11
05-JAN-11
06-JAN-11        129
07-JAN-11
08-JAN-11
09-JAN-11
10-JAN-11         87
11-JAN-11

10 rows selected.
You have to learn new things…

ops$tkyte%ORA11GR2> select dt, val,
  2         case when val is not null
  3             then to_char(row_number() over (order by dt),'fm0000')||val
  4             end max_val
  5    from t
  6   order by dt;

DT               VAL MAX_VAL
--------- ---------- ---------------------------------------------
02-JAN-11        195 0001195
03-JAN-11
04-JAN-11
05-JAN-11
06-JAN-11        129 0005129
07-JAN-11
08-JAN-11
09-JAN-11
10-JAN-11         87 000987
11-JAN-11

10 rows selected.
You have to learn new things…
ops$tkyte%ORA11GR2> select dt, val,
  2         max(max_val) over (order by dt) max_val_str
  3    from ( select dt, val,
  4                  case when val is not null
  5                  then to_char(row_number() over (order by dt),'fm0000')||val
  6                   end max_val
  7    from t ) order by dt
  8 /

DT               VAL MAX_VAL_STR
--------- ---------- ---------------------------------------------
02-JAN-11        195 0001195
03-JAN-11            0001195
04-JAN-11            0001195
05-JAN-11            0001195
06-JAN-11        129 0005129
07-JAN-11            0005129
08-JAN-11            0005129
09-JAN-11            0005129
10-JAN-11         87 000987
11-JAN-11            000987

10 rows selected.
You have to learn new things…
ops$tkyte%ORA11GR2> select dt, val,
  2         to_number(substr(max(max_val) over (order by dt),5)) max_val
  3    from ( select dt, val,
  4                  case when val is not null
  5                  then to_char(row_number() over (order by dt),'fm0000')||val
  6                   end max_val
  7    from t ) order by dt
  8 /

DT               VAL    MAX_VAL
--------- ---------- ----------
02-JAN-11        195        195
03-JAN-11                   195
04-JAN-11                   195
05-JAN-11                   195
06-JAN-11        129        129
07-JAN-11                   129
08-JAN-11                   129
09-JAN-11                   129
10-JAN-11         87         87
11-JAN-11                    87

10 rows selected.
You have to learn new things…

ops$tkyte%ORA11GR2> select dt, val,
  2         last_value(val ignore nulls) over (order by dt) val
  3    from t
  4   order by dt
  5 /

DT               VAL        VAL
--------- ---------- ----------
02-JAN-11        195        195
03-JAN-11                   195
04-JAN-11                   195
05-JAN-11                   195
06-JAN-11        129        129
07-JAN-11                   129
08-JAN-11                   129
09-JAN-11                   129
10-JAN-11         87         87
11-JAN-11                    87

10 rows selected.
Things Change
 begin
      for x in
      ( select *
           from big_table.big_table
         where rownum <= 10000 )
      loop
           null;
      end loop;
 end;
Things Change
declare
     type array is table of big_table%rowtype;
     l_data array;
     cursor c is
            select * from big_table where rownum <= 1000;
begin
     open c;
     loop
          fetch c bulk collect into l_data limit 100;
          for i in 1 .. l_data.count
          loop
               null;
          end loop;
          exit when c%notfound;
     end loop;
     close c;
end;
Things Change 9i

SELECT * FROM BIG_TABLE.BIG_TABLE WHERE ROWNUM <= 10000

call     count        cpu    elapsed      query       rows
------- ------   -------- ---------- ---------- ----------
Parse        1       0.01       0.00          0          0
Execute      1       0.00       0.00          0          0
Fetch    10001       0.15       0.17      10005      10000
------- ------   -------- ---------- ---------- ----------
total    10003       0.16       0.17      10005      10000
Things Change 10g

SELECT * FROM BIG_TABLE.BIG_TABLE WHERE ROWNUM <= 10000

call     count        cpu    elapsed      query       rows
------- ------   -------- ---------- ---------- ----------
Parse        1       0.00       0.00          0          0
Execute      1       0.00       0.00          0          0
Fetch      101       0.05       0.07        152      10000
------- ------   -------- ---------- ---------- ----------
total      103       0.05       0.07        152      10000
Using ROWNUM

•  Psuedo Column – not a “real” column
•  Assigned after the predicate (sort of during) but
   before any sort/aggregation

Select x,y
  from t where rownum < 10
 order by x
Versus
Select * from
 (select x,y from t order by x)
 where rownum < 10
Using ROWNUM
•  Incremented after a successful output
Select * from t where rownum = 2

Rownum = 1
For x in ( select * from t )
Loop
   if ( rownum = 2 )
   then
      output record
      rownum = rownum+1;
   end if
End loop
Using ROWNUM
•  Top-N queries
Select *
   from (select * from t where … order by X )
  where rownum <= 10;

•    Does not have to sort the entire set
•    Sets up an “array” conceptually
•    Gets the first 10
•    When we get the 11th, see if it is in the top 10
       –    If so, push out an existing array element, slide this in
       –    Else throw it out and get the next one.
•  Do not attempt this in CODE! (well – what about 10g?)
                                                                       rn02.sql
Top-N
ops$tkyte%ORA11GR2> explain plan for
  2 select * from (select * from scott.emp order by sal desc) where rownum <= 10;

Explained.
ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------
Plan hash value: 1744961472

--------------------------------------------------------------------------------
| Id | Operation                | Name | Rows | Bytes | Cost (%CPU)| Time      |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |    10 |   870 |     4 (25)| 00:00:01 |
|* 1 | COUNT STOPKEY            |      |       |       |            |          |
|   2 |   VIEW                  |      |    14 | 1218 |      4 (25)| 00:00:01 |
|* 3 |     SORT ORDER BY STOPKEY|      |    14 |   532 |     4 (25)| 00:00:01 |
|   4 |     TABLE ACCESS FULL   | EMP |     14 |   532 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(ROWNUM<=10)
   3 - filter(ROWNUM<=10)

17 rows selected.
Top-N
    ops$tkyte%ORA11GR2> @mystat "sorts (disk)"
    NAME                        VALUE
    ---------------------- ----------
    sorts (disk)                    0

    ops$tkyte%ORA11GR2>   select *
      2    from (select   *
      3            from   big_table.big_table
      4           order   by object_id) where rownum <= 10;
    10 rows selected.

    Statistics
    ----------------------------------------------------------
     …         1 sorts (memory)
               0 sorts (disk)
             10 rows processed

    ops$tkyte%ORA11GR2> @mystat2
    NAME                         VALUE DIFF
    ---------------------- ---------- ------------------
    sorts (disk)                     0               0
Top-N
 ops$tkyte%ORA11GR2> @mystat "sorts (disk)"
 NAME                        VALUE
 ---------------------- ----------
 sorts (disk)                    0

 ops$tkyte%ORA11GR2> declare
   2     cursor c is
   3        select * from big_table.big_table order by object_id;
   4     l_rec big_table_v%rowtype;
   5 begin
   6     open c;
   7     for i in 1 .. 10
   8     loop
   9          fetch c into l_rec;
  10     end loop;
  11     close c;
  12 end;
  13 /
 PL/SQL procedure successfully completed.

 ops$tkyte%ORA11GR2> @mystat2
 NAME                         VALUE DIFF
 ---------------------- ---------- ------------------
 sorts (disk)                     1               1
Top-N

ops$tkyte%ORA11GR2> create index bt_idx on
                    big_table.big_table(object_id) ;

Index created.
Top-N
select *
  from (select *
          from big_table.big_table
         order by object_id) where rownum <= 10

call     count        cpu    elapsed       disk      query    current         rows
------- ------   -------- ---------- ---------- ---------- ----------   ----------
Parse        1       0.00       0.00          0          0          0            0
Execute      1       0.00       0.00          0          0          0            0
Fetch        2       0.00       0.00          2         14          0           10
------- ------   -------- ---------- ---------- ---------- ----------   ----------
total        4       0.00       0.00          2         14          0           10

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 189
Number of plan statistics captured: 1

Row Source Operation
---------------------------------------------------
COUNT STOPKEY (cr=14 pr=2 pw=0 time=328 us)
  VIEW (cr=14 pr=2 pw=0 time=321 us cost=13 size=1410 card=10)
    TABLE ACCESS BY INDEX ROWID BIG_TABLE (cr=14 pr=2 pw=0 time=316 us …)
      INDEX FULL SCAN BT_IDX (cr=4 pr=2 pw=0 time=322 us cost=3 size=0 …)
Top-N

SELECT * FROM    BIG_TABLE.BIG_TABLE ORDER BY OBJECT_ID

call     count         cpu    elapsed       disk      query    current            rows
------- ------    -------- ---------- ---------- ---------- ----------      ----------
Parse        1        0.00       0.00          0          0          0               0
Execute      2        0.00       0.00          0          0          0               0
Fetch       10        1.12       2.17      14703      14544          7              10
------- ------    -------- ---------- ---------- ---------- ----------      ----------
total       13        1.12       2.17      14703      14544          7              10

Row Source Operation
---------------------------------------------------
SORT ORDER BY (cr=14544 pr=14703 pw=14639 time=2173284 us cost=26523 size...)
 TABLE ACCESS FULL BIG_TABLE (cr=14544 pr=14541 pw=0 time=694199 us cost=...)

Elapsed times include waiting on following events:
  Event waited on                             Times        Max. Wait   Total Waited
  ----------------------------------------   Waited       ----------   ------------
  direct path write temp                        476             0.00           1.23
  direct path read temp                            6            0.00           0.00
Using ROWNUM

•  Pagination

Select *
  From ( select      a.*, ROWNUM rnum
           From      ( your_query_goes_here ) a
          Where      ROWNUM <= :MAX_ROW_TO_FETCH )
 Where rnum >=       :MIN_ROW_TO_FETCH;

•  Everything from prior slide goes here…
•  Never ever let them “count the rows”, never.
•  Do not attempt this in CODE!
                                                  rn03.sql
Pagination

ops$tkyte%ORA11GR2>   set autotrace traceonly statistics
ops$tkyte%ORA11GR2>   variable max number
ops$tkyte%ORA11GR2>   variable min number
ops$tkyte%ORA11GR2>   exec :min := 100; :max := 115;

PL/SQL procedure successfully completed.
Pagination
select *
  from (select a.*, rownum rnum
          from (select /*+ FIRST_ROWS(15) */ *
                  from big_table.big_table order by object_id) a
         where rownum <= :Max)
 where rnum >= :min

call     count        cpu    elapsed       disk      query    current         rows
------- ------   -------- ---------- ---------- ---------- ----------   ----------
Parse        1       0.00       0.00          0          0          0            0
Execute      1       0.00       0.00          0          0          0            0
Fetch        3       0.00       0.00         12        119          0           16
------- ------   -------- ---------- ---------- ---------- ----------   ----------
total        5       0.00       0.00         12        119          0           16

Row Source Operation
---------------------------------------------------
VIEW (cr=119 pr=12 pw=0 time=939 us cost=18 size=2310 card=15)
 COUNT STOPKEY (cr=119 pr=12 pw=0 time=2840 us)
  VIEW (cr=119 pr=12 pw=0 time=2722 us cost=18 size=2115 card=15)
   TABLE ACCESS BY INDEX ROWID BIG_TABLE (cr=119 pr=12 pw=0 time=2605 us cost...)
    INDEX FULL SCAN BT_IDX (cr=4 pr=2 pw=0 time=258 us cost=3 size=0 card=...)
Pagination
ops$tkyte%ORA11GR2> declare
  2     cursor c is
  3        select * from big_table.big_table order by object_id;
  4     l_rec big_table_v%rowtype;
  5 begin
  6     open c;
  7     for i in 1 .. 115
  8     loop
  9          fetch c into l_rec;
 10          if ( i < 100 )
 11          then
 12               null;
 13          else
 14           null; -- process it
 15          end if;
 16     end loop;
 17     close c;
 18 end;
 19 /

PL/SQL procedure successfully completed.
Pagination

SELECT * FROM BIG_TABLE.BIG_TABLE ORDER BY OBJECT_ID

call     count        cpu    elapsed       disk      query    current          rows
------- ------   -------- ---------- ---------- ---------- ----------    ----------
Parse        1       0.00       0.00          0          0          0             0
Execute      2       0.00       0.00          0          0          0             0
Fetch      115       1.20       2.32      14703      14544          7           115
------- ------   -------- ---------- ---------- ---------- ----------    ----------
total      118       1.21       2.32      14703      14544          7           115

Row Source Operation
---------------------------------------------------
SORT ORDER BY (cr=14544 pr=14703 pw=14639 time=2324724 us cost=26523 size=...)
 TABLE ACCESS FULL BIG_TABLE (cr=14544 pr=14541 pw=0 time=682159 us cost=...)

Elapsed times include waiting on following events:
  Event waited on                             Times     Max. Wait   Total Waited
  ----------------------------------------   Waited    ----------   ------------
  direct path write temp                        571          0.00           1.34
  direct path read temp                            6         0.00           0.00
Scalar Subqueries

•  The ability to use a single column, single row query
   where you would normally use a “value”
Select dname, ‘Some Value’




From dept
•  That example shows a possible use of scalar
   subquerys – outer join removal
Scalar Subqueries

•  The ability to use a single column, single row query
   where you would normally use a “value”
Select dname, (select count(*)
                         from emp
                        where emp.deptno =
                               :dept.deptno ) cnt
From dept
•  That example shows a possible use of scalar
   subquerys – outer join removal
Scalar Subqueries

•  Outer join removal for “fast return” queries
    –    That works great for a single column
    –    What about when you need more than one?




                                                   ss01.sql
Scalar Subqueries
ops$tkyte%ORA11GR2> select *
  2    from (
  3 select a.owner, count(b.owner)
  4    from big_table.big_table_owners a left join big_table.big_table b
  5      on (a.owner = b.owner and b.object_type = 'TABLE' )
  6   group by a.owner
  7   order by a.owner
  8         )
  9   where rownum <= 2
 10 /

Statistics
----------------------------------------------------------
      14613 consistent gets
      14541 physical reads
           7 sorts (memory)
           0 sorts (disk)
           2 rows processed
Scalar Subqueries

ops$tkyte%ORA11GR2> select a.*,
  2         (select count(*)
  3             from big_table.big_table b
  4           where b.owner = a.owner and b.object_type = 'TABLE' ) cnt
  5    from (
  6 select a.owner
  7    from big_table.big_table_owners a
  8   order by a.owner
  9         ) a
 10   where rownum <= 2
 11 /

Statistics
----------------------------------------------------------
        590 consistent gets
           1 sorts (memory)
           0 sorts (disk)
           2 rows processed
Scalar Subqueries
ops$tkyte%ORA11GR2> select a.*,
  2         (select count(*)
  3             from big_table.big_table b
  4           where b.owner = a.owner and b.object_type = 'TABLE' ) cnt,
  5         (select min(created)
  6             from big_table.big_table b
  7           where b.owner = a.owner and b.object_type = 'TABLE' ) min_created,
  8         (select max(created)
  9             from big_table.big_table b
 10           where b.owner = a.owner and b.object_type = 'TABLE' ) max_created
 11    from (
 12 select a.owner
 13    from big_table.big_table_owners a
 14   order by a.owner
 15         ) a
 16   where rownum <= 2
 17 /

Statistics
----------------------------------------------------------
       1766 consistent gets
           1 sorts (memory)
           0 sorts (disk)
           2 rows processed
Scalar Subqueries
ops$tkyte%ORA11GR2> select owner,
  2          to_number(substr(data,1,10)) cnt,
  3          to_date(substr(data,11,14),'yyyymmddhh24miss') min_created,
  4          to_date(substr(data,25),'yyyymmddhh24miss') max_created
  5    from (
  6 select owner,
  7          (select to_char( count(*), 'fm0000000000') ||
  8                   to_char( min(created),'yyyymmddhh24miss') ||
  9                   to_char( max(created),'yyyymmddhh24miss')
 10              from big_table.big_table b
 11            where b.owner = a.owner and b.object_type = 'TABLE' ) data
 12    from (
 13 select a.owner
 14    from big_table.big_table_owners a
 15   order by a.owner
 16          ) a
 17   where rownum <= 2
 18          )
 19 /
Statistics
----------------------------------------------------------
        590 consistent gets
           1 sorts (memory)
           0 sorts (disk)
           2 rows processed
Scalar Subqueries

ops$tkyte%ORA11GR2> create or replace type myType as object
  2 ( cnt number, min_created date, max_created date )
  3 /

Type created.
Scalar Subqueries
ops$tkyte%ORA11GR2> select owner, a.data.cnt, a.data.min_created,
                            a.data.max_created
  2    from (
  3 select owner,
  4         (select myType( count(*), min(created), max(created) )
  5             from big_table.big_table b
  6           where b.owner = a.owner and b.object_type = 'TABLE' ) data
  7    from (
  8 select a.owner
  9    from big_table.big_table_owners a
 10   order by a.owner
 11         ) a
 12   where rownum <= 2
 13         ) a
 14 /

Statistics
----------------------------------------------------------
        590 consistent gets
           1 sorts (memory)
           0 sorts (disk)
           2 rows processed
Scalar Subqueries

•  Reducing PLSQL function calls via scalar subquery
   caching

Select * from t where x = pkg.getval()
versus
Select * from t where x =
     (select pkg.getval() from dual)

•  How to call them (scalar subqueries) “as little as
   possible”
                                             ss02.sql
Scalar Subqueries

ops$tkyte%ORA11GR2> create or replace function f( x in varchar2 ) return number
  2 as
  3 begin
  4          dbms_application_info.set_client_info(userenv('client_info')+1 );
  5          return length(x);
  6 end;
  7 /

Function created.
Scalar Subqueries

ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time;
                          dbms_application_info.set_client_info(0);
PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> select owner, f(owner) from stage;

72841 rows selected.

ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs,
                           userenv('client_info') from dual;

 CPU_HSECS USERENV('CLIENT_INFO')
---------- ----------------------------------------------------------
       111 72841
Scalar Subqueries

ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time;
                         dbms_application_info.set_client_info(0);
PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> select owner, (select f(owner) from dual) f
                      from stage;

72841 rows selected.

ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs,
                           userenv('client_info') from dual;

 CPU_HSECS USERENV('CLIENT_INFO')
---------- --------------------------------------------
        30 66
Scalar Subqueries

ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time;
                         dbms_application_info.set_client_info(0);

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> select owner, (select f(owner) from dual) f
  2    from (select owner, rownum r from stage order by owner);

72841 rows selected.

ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs,
                           userenv('client_info') from dual;

 CPU_HSECS USERENV('CLIENT_INFO')
---------- --------------------------------------------------------------
        32 32
Scalar Subqueries

ops$tkyte%ORA11GR2> create or replace function f( x in varchar2 ) return number
  2 DETERMINISTIC
  3 as
  4 begin
  5          dbms_application_info.set_client_info(userenv('client_info')+1 );
  6          return length(x);
  7 end;
  8 /

Function created.
Scalar Subqueries

ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time;
                         dbms_application_info.set_client_info(0);

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> select owner, f(owner) from stage;

72841 rows selected.

ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs,
                           userenv('client_info') from dual;

 CPU_HSECS USERENV('CLIENT_INFO')
---------- --------------------------------------------------------------
        73 8316
Scalar Subqueries

ops$tkyte%ORA11GR2> create or replace function f( x in varchar2 ) return number
  2 RESULT_CACHE
  3 as
  4 begin
  5          dbms_application_info.set_client_info(userenv('client_info')+1 );
  6          return length(x);
  7 end;
  8 /

Function created.
Scalar Subqueries

ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time;
                         dbms_application_info.set_client_info(0);

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> select owner, f(owner) from stage;

72841 rows selected.

ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs,
                           userenv('client_info') from dual;

 CPU_HSECS USERENV('CLIENT_INFO')
---------- ----------------------------------------------------------
        64 32
Scalar Subqueries

ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time;
                         dbms_application_info.set_client_info(0);

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> select owner, f(owner) from stage;
72841 rows selected.


ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs,
                           userenv('client_info') from dual;

 CPU_HSECS USERENV('CLIENT_INFO')
---------- -----------------------------------------------------------
        69 0
Scalar Subqueries

ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time;
                         dbms_application_info.set_client_info(0);

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> select owner, (select f(owner) from dual) from stage;
72841 rows selected.


ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs, user

 CPU_HSECS USERENV('CLIENT_INFO')
---------- --------------------------------------------------------------
        19 0
Don’t tune queries!
Think in SETS!
Questions

More Related Content

PDF
Database & Technology 1 _ Tom Kyte _ Efficient PL SQL - Why and How to Use.pdf
PPTX
SQL Tuning, takes 3 to tango
PPTX
Managing Unstructured Data: Lobs in the World of JSON
PDF
The Challenges of Distributing Postgres: A Citus Story
PPTX
Optimizing queries MySQL
PDF
Scaling MySQL Strategies for Developers
PDF
MySQL Query tuning 101
PDF
Noinject
Database & Technology 1 _ Tom Kyte _ Efficient PL SQL - Why and How to Use.pdf
SQL Tuning, takes 3 to tango
Managing Unstructured Data: Lobs in the World of JSON
The Challenges of Distributing Postgres: A Citus Story
Optimizing queries MySQL
Scaling MySQL Strategies for Developers
MySQL Query tuning 101
Noinject

What's hot (20)

PDF
12c SQL Plan Directives
PDF
Summary tables with flexviews
PPTX
Oracle Database 12.1.0.2 New Features
PDF
Oracle table lock modes
PDF
Performance Schema for MySQL Troubleshooting
PDF
Mysql Explain Explained
PDF
Introduction to MySQL Query Tuning for Dev[Op]s
PDF
Why Use EXPLAIN FORMAT=JSON?
PDF
Maximizing SQL Reviews and Tuning with pt-query-digest
PPTX
DBA Brasil 1.0 - DBA Commands and Concepts That Every Developer Should Know
PDF
All on Adaptive and Extended Cursor Sharing
PDF
Introduction into MySQL Query Tuning for Dev[Op]s
PDF
New features in Performance Schema 5.7 in action
PPTX
Ukoug15 SIMD outside and inside Oracle 12c (12.1.0.2)
PDF
Understanding Query Execution
PDF
MySQL 8.0 EXPLAIN ANALYZE
PDF
Performance Schema for MySQL Troubleshooting
PDF
Introduction into MySQL Query Tuning
PDF
Basic MySQL Troubleshooting for Oracle Database Administrators
PDF
エンタープライズ・クラウドと 並列・分散・非同期処理
12c SQL Plan Directives
Summary tables with flexviews
Oracle Database 12.1.0.2 New Features
Oracle table lock modes
Performance Schema for MySQL Troubleshooting
Mysql Explain Explained
Introduction to MySQL Query Tuning for Dev[Op]s
Why Use EXPLAIN FORMAT=JSON?
Maximizing SQL Reviews and Tuning with pt-query-digest
DBA Brasil 1.0 - DBA Commands and Concepts That Every Developer Should Know
All on Adaptive and Extended Cursor Sharing
Introduction into MySQL Query Tuning for Dev[Op]s
New features in Performance Schema 5.7 in action
Ukoug15 SIMD outside and inside Oracle 12c (12.1.0.2)
Understanding Query Execution
MySQL 8.0 EXPLAIN ANALYZE
Performance Schema for MySQL Troubleshooting
Introduction into MySQL Query Tuning
Basic MySQL Troubleshooting for Oracle Database Administrators
エンタープライズ・クラウドと 並列・分散・非同期処理
Ad

Viewers also liked (14)

PPT
What Are We Still Doing Wrong
PPT
Eff Plsql
PPT
Oracle PL/SQL Bulk binds
PPTX
SikuliX
PPTX
Common.logging
PPTX
Web deploy
PPTX
PL/SQL & SQL CODING GUIDELINES – Part 4
PPTX
Fx.configuration
PPTX
Regular expression
PPTX
PL/SQL & SQL CODING GUIDELINES – Part 5
PPTX
Web deploy command line
PPTX
Visual studio 2017
PDF
Advanced PL/SQL Optimizing for Better Performance 2016
PPTX
SonarQube - The leading platform for Continuous Code Quality
What Are We Still Doing Wrong
Eff Plsql
Oracle PL/SQL Bulk binds
SikuliX
Common.logging
Web deploy
PL/SQL & SQL CODING GUIDELINES – Part 4
Fx.configuration
Regular expression
PL/SQL & SQL CODING GUIDELINES – Part 5
Web deploy command line
Visual studio 2017
Advanced PL/SQL Optimizing for Better Performance 2016
SonarQube - The leading platform for Continuous Code Quality
Ad

Similar to Database & Technology 1 _ Tom Kyte _ SQL Techniques.pdf (20)

PDF
APEX Connect 2019 - array/bulk processing in PLSQL
PDF
Query optimizer vivek sharma
PDF
Quick Wins
PDF
Write Faster SQL with Trino.pdf
PDF
Database & Technology 2 _ Richard Foote _ 10 things you probably dont know ab...
PPTX
Analyzing SQL Traces generated by EVENT 10046.pptx
PDF
Discard inport exchange table & tablespace
PDF
OTN tour 2015 AWR data mining
PPTX
Real World Performance - Data Warehouses
PPTX
AWR DB performance Data Mining - Collaborate 2015
ODP
Beyond PHP - it's not (just) about the code
DOC
SQLQueries
PDF
Beyond php - it's not (just) about the code
PDF
Hotsos 2011: Mining the AWR repository for Capacity Planning, Visualization, ...
PDF
Oracle10g New Features I
PDF
In Memory Database In Action by Tanel Poder and Kerry Osborne
PDF
Oracle Database In-Memory Option in Action
PPT
zen and the art of SQL optimization
PPT
Top 10 Oracle SQL tuning tips
PPTX
Understanding Query Optimization with ‘regular’ and ‘Exadata’ Oracle
APEX Connect 2019 - array/bulk processing in PLSQL
Query optimizer vivek sharma
Quick Wins
Write Faster SQL with Trino.pdf
Database & Technology 2 _ Richard Foote _ 10 things you probably dont know ab...
Analyzing SQL Traces generated by EVENT 10046.pptx
Discard inport exchange table & tablespace
OTN tour 2015 AWR data mining
Real World Performance - Data Warehouses
AWR DB performance Data Mining - Collaborate 2015
Beyond PHP - it's not (just) about the code
SQLQueries
Beyond php - it's not (just) about the code
Hotsos 2011: Mining the AWR repository for Capacity Planning, Visualization, ...
Oracle10g New Features I
In Memory Database In Action by Tanel Poder and Kerry Osborne
Oracle Database In-Memory Option in Action
zen and the art of SQL optimization
Top 10 Oracle SQL tuning tips
Understanding Query Optimization with ‘regular’ and ‘Exadata’ Oracle

More from InSync2011 (20)

PDF
Developer & Fusion Middleware 2 _ Scott Robertson _ SOA, Portals and Enterpri...
PDF
New & Emerging _ KrisDowney _ Simplifying the Change Process.pdf
PDF
Oracle Systems _ Kevin McIsaac _The IT landscape has changed.pdf
PDF
Reporting _ Scott Tunbridge _ Op Mgmt to Perf Excel.pdf
PDF
Developer and Fusion Middleware 2 _ Scott Robertson _ SOA, portals and entepr...
PDF
Primavera _ Loretta Bayliss _ Implementing EPPM in rapidly changing and compe...
PDF
Database & Technology 1 _ Martin Power _ Delivering Oracles hight availabilit...
PDF
Database & Technology 1 _ Craig Shallahamer _ Unit of work time based perform...
PDF
Database & Technology 1 _ Marcelle Kratchvil _ Why you should be storing unst...
PDF
Database & Technology 1 _ Milina Ristic _ Why use oracle data guard.pdf
PDF
Database & Technology 1 _ Clancy Bufton _ Flashback Query - oracle total reca...
PDF
Databse & Technology 2 _ Francisco Munoz Alvarez _ Oracle Security Tips - Som...
PDF
Databse & Technology 2 _ Francisco Munoz alvarez _ 11g new functionalities fo...
PDF
Databse & Technology 2 | Connor McDonald | Managing Optimiser Statistics - A ...
PDF
Databse & Technology 2 _ Shan Nawaz _ Oracle 11g Top 10 features - not your u...
PDF
Databse & Technology 2 _ Paul Guerin _ The biggest looser database - a boot c...
PDF
Developer and Fusion Middleware 1 _ Kevin Powe _ Log files - a wealth of fore...
PDF
Developer and Fusion Middleware 2 _ Aaron Blishen _ Event driven SOA Integrat...
PDF
Developer and Fusion Middleware 2 _Greg Kirkendall _ How Australia Post teach...
PDF
Developer and Fusion Middleware 1 _ Paul Ricketts _ Paper Process Automation ...
Developer & Fusion Middleware 2 _ Scott Robertson _ SOA, Portals and Enterpri...
New & Emerging _ KrisDowney _ Simplifying the Change Process.pdf
Oracle Systems _ Kevin McIsaac _The IT landscape has changed.pdf
Reporting _ Scott Tunbridge _ Op Mgmt to Perf Excel.pdf
Developer and Fusion Middleware 2 _ Scott Robertson _ SOA, portals and entepr...
Primavera _ Loretta Bayliss _ Implementing EPPM in rapidly changing and compe...
Database & Technology 1 _ Martin Power _ Delivering Oracles hight availabilit...
Database & Technology 1 _ Craig Shallahamer _ Unit of work time based perform...
Database & Technology 1 _ Marcelle Kratchvil _ Why you should be storing unst...
Database & Technology 1 _ Milina Ristic _ Why use oracle data guard.pdf
Database & Technology 1 _ Clancy Bufton _ Flashback Query - oracle total reca...
Databse & Technology 2 _ Francisco Munoz Alvarez _ Oracle Security Tips - Som...
Databse & Technology 2 _ Francisco Munoz alvarez _ 11g new functionalities fo...
Databse & Technology 2 | Connor McDonald | Managing Optimiser Statistics - A ...
Databse & Technology 2 _ Shan Nawaz _ Oracle 11g Top 10 features - not your u...
Databse & Technology 2 _ Paul Guerin _ The biggest looser database - a boot c...
Developer and Fusion Middleware 1 _ Kevin Powe _ Log files - a wealth of fore...
Developer and Fusion Middleware 2 _ Aaron Blishen _ Event driven SOA Integrat...
Developer and Fusion Middleware 2 _Greg Kirkendall _ How Australia Post teach...
Developer and Fusion Middleware 1 _ Paul Ricketts _ Paper Process Automation ...

Database & Technology 1 _ Tom Kyte _ SQL Techniques.pdf

  • 2. Who am I •  Been with Oracle since 1993 •  User of Oracle since 1987 •  The “Tom” behind AskTom in Oracle Magazine www.oracle.com/oramag •  Expert Oracle Database Architecture •  Effective Oracle by Design •  Expert One on One Oracle •  Beginning Oracle
  • 3. Agenda •  What do you need to write “good” SQL •  The Schema Matters •  Knowing what is available –  Using rownum (yes, to 'tune') –  Scalar subqueries –  Analytics –  Some hints •  Don’t tune queries! •  Other things –  Materialized Views –  With subquery factoring –  Merge –  …
  • 4. What do you need to know… •  Access Paths –  There are a lot of them –  There is no best one (else there would be, well, one) •  A little bit of physics –  Full scans are not evil –  Indexes are not all goodness •  How the data is managed by Oracle –  high water marks for example –  IOT’s, clusters, etc •  What your query needs to actually do –  Is that outer join really necessary or “just in case”
  • 5. Structures •  How the data is accessed and organized makes a difference –  Clustering Select * from orders o, line_items li ORDERS ORDERS & LINE where o.order# = li.order# LINE ITEMS ITEMS And o.order# = :order
  • 6. Structures •  How the data is accessed and organized makes a difference –  Clustering –  Index Organized Tables Select avg(price) STOCKS STOCKS From stocks Where symbol = ‘ORCL’ And stock_dt >= sysdate-5;
  • 7. Structures •  How the data is accessed and organized makes a difference –  Clustering –  Index Organized Tables ORDERS ORDERS ORDERS –  Partitioning Europe USA Jan Jan Feb Feb Composite Partition Partition Large Table Higher and Manage Divide Performance Difficult to Conquer More flexibility to match Easier to Manage business needs Improve Performance
  • 8. The Schema Matters •  A Lot! •  Tune this query: Select DOCUMENT_NAME, META_DATA from documents where userid=:x; •  That is about as easy as it gets (the SQL) •  Not too much we can do to rewrite it… •  But we’d like to make it better. Iot01.sql Cf.sql
  • 9. Organization Counts ops$tkyte%ORA11GR2> create table iot 2 ( username varchar2(30), 3 document_name varchar2(30), 4 other_data char(1000), 5 constraint iot_pk primary key (username,document_name)) 6 organization index 7 / Table created. ops$tkyte%ORA11GR2> create table heap 2 ( username varchar2(30), 3 document_name varchar2(30), 4 other_data char(1000), 5 constraint heap_pk primary key (username,document_name)) 6 / Table created.
  • 10. Organization Counts ops$tkyte%ORA11GR2> begin 2 for i in 1 .. 100 3 loop 4 for x in ( select username from all_users ) 5 loop 6 insert into heap 7 (username,document_name,other_data) values 8 ( x.username, x.username || '_' || i, 'x' ); 9 10 insert into iot 11 (username,document_name,other_data) values 12 ( x.username, x.username || '_' || i, 'x' ); 13 end loop; 14 end loop; 15 dbms_stats.gather_table_stats 16 ( user, 'IOT', cascade=>true ); 17 dbms_stats.gather_table_stats 18 ( user, 'HEAP', method_opt=>'for all indexed columns', cascade=>true ); 19 commit; 20 end; 21 /
  • 11. Organization Counts ops$tkyte%ORA11GR2> declare 2 l_rec heap%rowtype; 3 cursor heap_cursor(p_username in varchar2) is 4 select * from heap single_row where username = p_username; 5 cursor iot_cursor(p_username in varchar2) is 6 select * from iot single_row where username = p_username; 7 begin 8 for i in 1 .. 10 9 loop 10 for x in (select username from all_users) loop 11 open heap_cursor(x.username); 12 loop 13 fetch heap_cursor into l_rec; 14 exit when heap_cursor%notfound; 15 end loop; 16 close heap_cursor; 17 open iot_cursor(x.username); 18 loop 19 fetch iot_cursor into l_rec; 20 exit when iot_cursor%notfound; 21 end loop; 22 close iot_cursor; 23 end loop; 24 end loop; 25 end; 26 /
  • 12. Organization Counts ops$tkyte%ORA11GR2> declare 2 type array is table of iot%rowtype; 3 l_data array; 4 begin 5 for i in 1 .. 10 6 loop 7 for x in (select username from all_users) 8 loop 9 select * bulk collect into l_data 10 from heap bulk_collect 11 where username = x.username; 12 select * bulk collect into l_data 13 from iot bulk_collect 14 where username = x.username; 15 end loop; 16 end loop; 17 end; 18 / PL/SQL procedure successfully completed.
  • 13. Organization Counts SELECT * FROM HEAP SINGLE_ROW WHERE USERNAME = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 410 0.02 0.02 0 0 0 0 Fetch 41410 0.25 0.27 0 82810 0 41000 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 41821 0.28 0.30 0 82810 0 41000 Row Source Operation --------------------------------------------------- TABLE ACCESS BY INDEX ROWID HEAP (cr=202 pr=0 pw=0 time=41 us cost=102 ...) INDEX RANGE SCAN HEAP_PK (cr=102 pr=0 pw=0 time=221 us cost=2 size=0 ca...)
  • 14. Organization Counts SELECT * FROM IOT SINGLE_ROW WHERE USERNAME = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 410 0.02 0.02 0 0 0 0 Fetch 41410 0.16 0.18 0 42220 0 41000 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 41821 0.19 0.21 0 42220 0 41000 Row Source Operation --------------------------------------------------- INDEX RANGE SCAN IOT_PK (cr=103 pr=0 pw=0 time=33 us cost=21 size=102000 ...)
  • 15. Organization Counts SELECT * FROM HEAP BULK_COLLECT WHERE USERNAME = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 410 0.01 0.02 0 0 0 0 Fetch 410 0.11 0.12 0 42010 0 41000 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 821 0.13 0.14 0 42010 0 41000 Row Source Operation --------------------------------------------------- TABLE ACCESS BY INDEX ROWID HEAP (cr=102 pr=0 pw=0 time=533 us cost=102 ...) INDEX RANGE SCAN HEAP_PK (cr=2 pr=0 pw=0 time=23 us cost=2 size=0 ca...)
  • 16. Organization Counts SELECT * FROM IOT BULK_COLLECT WHERE USERNAME = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 410 0.01 0.02 0 0 0 0 Fetch 410 0.06 0.06 0 9000 0 41000 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 821 0.08 0.08 0 9000 0 41000 Row Source Operation --------------------------------------------------- INDEX RANGE SCAN IOT_PK (cr=22 pr=0 pw=0 time=142 us cost=21 size=102000...)
  • 17. Knowing what is available •  There is a lot out there… •  I learn something new every day •  Skimming the docs works –  Oh, I remember something similar… •  Check out the “whats new in” at the head of the docs •  Participate in the forums •  Things change… Some things must be “discovered” Ignulls.sql
  • 18. You have to learn new things… ops$tkyte%ORA11GR2> select dt, val 2 from t 3 order by dt; DT VAL --------- ---------- 02-JAN-11 195 03-JAN-11 04-JAN-11 05-JAN-11 06-JAN-11 129 07-JAN-11 08-JAN-11 09-JAN-11 10-JAN-11 87 11-JAN-11 10 rows selected.
  • 19. You have to learn new things… ops$tkyte%ORA11GR2> select dt, val, 2 case when val is not null 3 then to_char(row_number() over (order by dt),'fm0000')||val 4 end max_val 5 from t 6 order by dt; DT VAL MAX_VAL --------- ---------- --------------------------------------------- 02-JAN-11 195 0001195 03-JAN-11 04-JAN-11 05-JAN-11 06-JAN-11 129 0005129 07-JAN-11 08-JAN-11 09-JAN-11 10-JAN-11 87 000987 11-JAN-11 10 rows selected.
  • 20. You have to learn new things… ops$tkyte%ORA11GR2> select dt, val, 2 max(max_val) over (order by dt) max_val_str 3 from ( select dt, val, 4 case when val is not null 5 then to_char(row_number() over (order by dt),'fm0000')||val 6 end max_val 7 from t ) order by dt 8 / DT VAL MAX_VAL_STR --------- ---------- --------------------------------------------- 02-JAN-11 195 0001195 03-JAN-11 0001195 04-JAN-11 0001195 05-JAN-11 0001195 06-JAN-11 129 0005129 07-JAN-11 0005129 08-JAN-11 0005129 09-JAN-11 0005129 10-JAN-11 87 000987 11-JAN-11 000987 10 rows selected.
  • 21. You have to learn new things… ops$tkyte%ORA11GR2> select dt, val, 2 to_number(substr(max(max_val) over (order by dt),5)) max_val 3 from ( select dt, val, 4 case when val is not null 5 then to_char(row_number() over (order by dt),'fm0000')||val 6 end max_val 7 from t ) order by dt 8 / DT VAL MAX_VAL --------- ---------- ---------- 02-JAN-11 195 195 03-JAN-11 195 04-JAN-11 195 05-JAN-11 195 06-JAN-11 129 129 07-JAN-11 129 08-JAN-11 129 09-JAN-11 129 10-JAN-11 87 87 11-JAN-11 87 10 rows selected.
  • 22. You have to learn new things… ops$tkyte%ORA11GR2> select dt, val, 2 last_value(val ignore nulls) over (order by dt) val 3 from t 4 order by dt 5 / DT VAL VAL --------- ---------- ---------- 02-JAN-11 195 195 03-JAN-11 195 04-JAN-11 195 05-JAN-11 195 06-JAN-11 129 129 07-JAN-11 129 08-JAN-11 129 09-JAN-11 129 10-JAN-11 87 87 11-JAN-11 87 10 rows selected.
  • 23. Things Change begin for x in ( select * from big_table.big_table where rownum <= 10000 ) loop null; end loop; end;
  • 24. Things Change declare type array is table of big_table%rowtype; l_data array; cursor c is select * from big_table where rownum <= 1000; begin open c; loop fetch c bulk collect into l_data limit 100; for i in 1 .. l_data.count loop null; end loop; exit when c%notfound; end loop; close c; end;
  • 25. Things Change 9i SELECT * FROM BIG_TABLE.BIG_TABLE WHERE ROWNUM <= 10000 call count cpu elapsed query rows ------- ------ -------- ---------- ---------- ---------- Parse 1 0.01 0.00 0 0 Execute 1 0.00 0.00 0 0 Fetch 10001 0.15 0.17 10005 10000 ------- ------ -------- ---------- ---------- ---------- total 10003 0.16 0.17 10005 10000
  • 26. Things Change 10g SELECT * FROM BIG_TABLE.BIG_TABLE WHERE ROWNUM <= 10000 call count cpu elapsed query rows ------- ------ -------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 Execute 1 0.00 0.00 0 0 Fetch 101 0.05 0.07 152 10000 ------- ------ -------- ---------- ---------- ---------- total 103 0.05 0.07 152 10000
  • 27. Using ROWNUM •  Psuedo Column – not a “real” column •  Assigned after the predicate (sort of during) but before any sort/aggregation Select x,y from t where rownum < 10 order by x Versus Select * from (select x,y from t order by x) where rownum < 10
  • 28. Using ROWNUM •  Incremented after a successful output Select * from t where rownum = 2 Rownum = 1 For x in ( select * from t ) Loop if ( rownum = 2 ) then output record rownum = rownum+1; end if End loop
  • 29. Using ROWNUM •  Top-N queries Select * from (select * from t where … order by X ) where rownum <= 10; •  Does not have to sort the entire set •  Sets up an “array” conceptually •  Gets the first 10 •  When we get the 11th, see if it is in the top 10 –  If so, push out an existing array element, slide this in –  Else throw it out and get the next one. •  Do not attempt this in CODE! (well – what about 10g?) rn02.sql
  • 30. Top-N ops$tkyte%ORA11GR2> explain plan for 2 select * from (select * from scott.emp order by sal desc) where rownum <= 10; Explained. ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------- Plan hash value: 1744961472 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10 | 870 | 4 (25)| 00:00:01 | |* 1 | COUNT STOPKEY | | | | | | | 2 | VIEW | | 14 | 1218 | 4 (25)| 00:00:01 | |* 3 | SORT ORDER BY STOPKEY| | 14 | 532 | 4 (25)| 00:00:01 | | 4 | TABLE ACCESS FULL | EMP | 14 | 532 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(ROWNUM<=10) 3 - filter(ROWNUM<=10) 17 rows selected.
  • 31. Top-N ops$tkyte%ORA11GR2> @mystat "sorts (disk)" NAME VALUE ---------------------- ---------- sorts (disk) 0 ops$tkyte%ORA11GR2> select * 2 from (select * 3 from big_table.big_table 4 order by object_id) where rownum <= 10; 10 rows selected. Statistics ---------------------------------------------------------- … 1 sorts (memory) 0 sorts (disk) 10 rows processed ops$tkyte%ORA11GR2> @mystat2 NAME VALUE DIFF ---------------------- ---------- ------------------ sorts (disk) 0 0
  • 32. Top-N ops$tkyte%ORA11GR2> @mystat "sorts (disk)" NAME VALUE ---------------------- ---------- sorts (disk) 0 ops$tkyte%ORA11GR2> declare 2 cursor c is 3 select * from big_table.big_table order by object_id; 4 l_rec big_table_v%rowtype; 5 begin 6 open c; 7 for i in 1 .. 10 8 loop 9 fetch c into l_rec; 10 end loop; 11 close c; 12 end; 13 / PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> @mystat2 NAME VALUE DIFF ---------------------- ---------- ------------------ sorts (disk) 1 1
  • 33. Top-N ops$tkyte%ORA11GR2> create index bt_idx on big_table.big_table(object_id) ; Index created.
  • 34. Top-N select * from (select * from big_table.big_table order by object_id) where rownum <= 10 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 2 0.00 0.00 2 14 0 10 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 4 0.00 0.00 2 14 0 10 Misses in library cache during parse: 1 Optimizer mode: ALL_ROWS Parsing user id: 189 Number of plan statistics captured: 1 Row Source Operation --------------------------------------------------- COUNT STOPKEY (cr=14 pr=2 pw=0 time=328 us) VIEW (cr=14 pr=2 pw=0 time=321 us cost=13 size=1410 card=10) TABLE ACCESS BY INDEX ROWID BIG_TABLE (cr=14 pr=2 pw=0 time=316 us …) INDEX FULL SCAN BT_IDX (cr=4 pr=2 pw=0 time=322 us cost=3 size=0 …)
  • 35. Top-N SELECT * FROM BIG_TABLE.BIG_TABLE ORDER BY OBJECT_ID call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 2 0.00 0.00 0 0 0 0 Fetch 10 1.12 2.17 14703 14544 7 10 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 13 1.12 2.17 14703 14544 7 10 Row Source Operation --------------------------------------------------- SORT ORDER BY (cr=14544 pr=14703 pw=14639 time=2173284 us cost=26523 size...) TABLE ACCESS FULL BIG_TABLE (cr=14544 pr=14541 pw=0 time=694199 us cost=...) Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ direct path write temp 476 0.00 1.23 direct path read temp 6 0.00 0.00
  • 36. Using ROWNUM •  Pagination Select * From ( select a.*, ROWNUM rnum From ( your_query_goes_here ) a Where ROWNUM <= :MAX_ROW_TO_FETCH ) Where rnum >= :MIN_ROW_TO_FETCH; •  Everything from prior slide goes here… •  Never ever let them “count the rows”, never. •  Do not attempt this in CODE! rn03.sql
  • 37. Pagination ops$tkyte%ORA11GR2> set autotrace traceonly statistics ops$tkyte%ORA11GR2> variable max number ops$tkyte%ORA11GR2> variable min number ops$tkyte%ORA11GR2> exec :min := 100; :max := 115; PL/SQL procedure successfully completed.
  • 38. Pagination select * from (select a.*, rownum rnum from (select /*+ FIRST_ROWS(15) */ * from big_table.big_table order by object_id) a where rownum <= :Max) where rnum >= :min call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 3 0.00 0.00 12 119 0 16 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 5 0.00 0.00 12 119 0 16 Row Source Operation --------------------------------------------------- VIEW (cr=119 pr=12 pw=0 time=939 us cost=18 size=2310 card=15) COUNT STOPKEY (cr=119 pr=12 pw=0 time=2840 us) VIEW (cr=119 pr=12 pw=0 time=2722 us cost=18 size=2115 card=15) TABLE ACCESS BY INDEX ROWID BIG_TABLE (cr=119 pr=12 pw=0 time=2605 us cost...) INDEX FULL SCAN BT_IDX (cr=4 pr=2 pw=0 time=258 us cost=3 size=0 card=...)
  • 39. Pagination ops$tkyte%ORA11GR2> declare 2 cursor c is 3 select * from big_table.big_table order by object_id; 4 l_rec big_table_v%rowtype; 5 begin 6 open c; 7 for i in 1 .. 115 8 loop 9 fetch c into l_rec; 10 if ( i < 100 ) 11 then 12 null; 13 else 14 null; -- process it 15 end if; 16 end loop; 17 close c; 18 end; 19 / PL/SQL procedure successfully completed.
  • 40. Pagination SELECT * FROM BIG_TABLE.BIG_TABLE ORDER BY OBJECT_ID call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 2 0.00 0.00 0 0 0 0 Fetch 115 1.20 2.32 14703 14544 7 115 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 118 1.21 2.32 14703 14544 7 115 Row Source Operation --------------------------------------------------- SORT ORDER BY (cr=14544 pr=14703 pw=14639 time=2324724 us cost=26523 size=...) TABLE ACCESS FULL BIG_TABLE (cr=14544 pr=14541 pw=0 time=682159 us cost=...) Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ direct path write temp 571 0.00 1.34 direct path read temp 6 0.00 0.00
  • 41. Scalar Subqueries •  The ability to use a single column, single row query where you would normally use a “value” Select dname, ‘Some Value’ From dept •  That example shows a possible use of scalar subquerys – outer join removal
  • 42. Scalar Subqueries •  The ability to use a single column, single row query where you would normally use a “value” Select dname, (select count(*) from emp where emp.deptno = :dept.deptno ) cnt From dept •  That example shows a possible use of scalar subquerys – outer join removal
  • 43. Scalar Subqueries •  Outer join removal for “fast return” queries –  That works great for a single column –  What about when you need more than one? ss01.sql
  • 44. Scalar Subqueries ops$tkyte%ORA11GR2> select * 2 from ( 3 select a.owner, count(b.owner) 4 from big_table.big_table_owners a left join big_table.big_table b 5 on (a.owner = b.owner and b.object_type = 'TABLE' ) 6 group by a.owner 7 order by a.owner 8 ) 9 where rownum <= 2 10 / Statistics ---------------------------------------------------------- 14613 consistent gets 14541 physical reads 7 sorts (memory) 0 sorts (disk) 2 rows processed
  • 45. Scalar Subqueries ops$tkyte%ORA11GR2> select a.*, 2 (select count(*) 3 from big_table.big_table b 4 where b.owner = a.owner and b.object_type = 'TABLE' ) cnt 5 from ( 6 select a.owner 7 from big_table.big_table_owners a 8 order by a.owner 9 ) a 10 where rownum <= 2 11 / Statistics ---------------------------------------------------------- 590 consistent gets 1 sorts (memory) 0 sorts (disk) 2 rows processed
  • 46. Scalar Subqueries ops$tkyte%ORA11GR2> select a.*, 2 (select count(*) 3 from big_table.big_table b 4 where b.owner = a.owner and b.object_type = 'TABLE' ) cnt, 5 (select min(created) 6 from big_table.big_table b 7 where b.owner = a.owner and b.object_type = 'TABLE' ) min_created, 8 (select max(created) 9 from big_table.big_table b 10 where b.owner = a.owner and b.object_type = 'TABLE' ) max_created 11 from ( 12 select a.owner 13 from big_table.big_table_owners a 14 order by a.owner 15 ) a 16 where rownum <= 2 17 / Statistics ---------------------------------------------------------- 1766 consistent gets 1 sorts (memory) 0 sorts (disk) 2 rows processed
  • 47. Scalar Subqueries ops$tkyte%ORA11GR2> select owner, 2 to_number(substr(data,1,10)) cnt, 3 to_date(substr(data,11,14),'yyyymmddhh24miss') min_created, 4 to_date(substr(data,25),'yyyymmddhh24miss') max_created 5 from ( 6 select owner, 7 (select to_char( count(*), 'fm0000000000') || 8 to_char( min(created),'yyyymmddhh24miss') || 9 to_char( max(created),'yyyymmddhh24miss') 10 from big_table.big_table b 11 where b.owner = a.owner and b.object_type = 'TABLE' ) data 12 from ( 13 select a.owner 14 from big_table.big_table_owners a 15 order by a.owner 16 ) a 17 where rownum <= 2 18 ) 19 / Statistics ---------------------------------------------------------- 590 consistent gets 1 sorts (memory) 0 sorts (disk) 2 rows processed
  • 48. Scalar Subqueries ops$tkyte%ORA11GR2> create or replace type myType as object 2 ( cnt number, min_created date, max_created date ) 3 / Type created.
  • 49. Scalar Subqueries ops$tkyte%ORA11GR2> select owner, a.data.cnt, a.data.min_created, a.data.max_created 2 from ( 3 select owner, 4 (select myType( count(*), min(created), max(created) ) 5 from big_table.big_table b 6 where b.owner = a.owner and b.object_type = 'TABLE' ) data 7 from ( 8 select a.owner 9 from big_table.big_table_owners a 10 order by a.owner 11 ) a 12 where rownum <= 2 13 ) a 14 / Statistics ---------------------------------------------------------- 590 consistent gets 1 sorts (memory) 0 sorts (disk) 2 rows processed
  • 50. Scalar Subqueries •  Reducing PLSQL function calls via scalar subquery caching Select * from t where x = pkg.getval() versus Select * from t where x = (select pkg.getval() from dual) •  How to call them (scalar subqueries) “as little as possible” ss02.sql
  • 51. Scalar Subqueries ops$tkyte%ORA11GR2> create or replace function f( x in varchar2 ) return number 2 as 3 begin 4 dbms_application_info.set_client_info(userenv('client_info')+1 ); 5 return length(x); 6 end; 7 / Function created.
  • 52. Scalar Subqueries ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time; dbms_application_info.set_client_info(0); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> select owner, f(owner) from stage; 72841 rows selected. ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs, userenv('client_info') from dual; CPU_HSECS USERENV('CLIENT_INFO') ---------- ---------------------------------------------------------- 111 72841
  • 53. Scalar Subqueries ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time; dbms_application_info.set_client_info(0); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> select owner, (select f(owner) from dual) f from stage; 72841 rows selected. ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs, userenv('client_info') from dual; CPU_HSECS USERENV('CLIENT_INFO') ---------- -------------------------------------------- 30 66
  • 54. Scalar Subqueries ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time; dbms_application_info.set_client_info(0); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> select owner, (select f(owner) from dual) f 2 from (select owner, rownum r from stage order by owner); 72841 rows selected. ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs, userenv('client_info') from dual; CPU_HSECS USERENV('CLIENT_INFO') ---------- -------------------------------------------------------------- 32 32
  • 55. Scalar Subqueries ops$tkyte%ORA11GR2> create or replace function f( x in varchar2 ) return number 2 DETERMINISTIC 3 as 4 begin 5 dbms_application_info.set_client_info(userenv('client_info')+1 ); 6 return length(x); 7 end; 8 / Function created.
  • 56. Scalar Subqueries ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time; dbms_application_info.set_client_info(0); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> select owner, f(owner) from stage; 72841 rows selected. ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs, userenv('client_info') from dual; CPU_HSECS USERENV('CLIENT_INFO') ---------- -------------------------------------------------------------- 73 8316
  • 57. Scalar Subqueries ops$tkyte%ORA11GR2> create or replace function f( x in varchar2 ) return number 2 RESULT_CACHE 3 as 4 begin 5 dbms_application_info.set_client_info(userenv('client_info')+1 ); 6 return length(x); 7 end; 8 / Function created.
  • 58. Scalar Subqueries ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time; dbms_application_info.set_client_info(0); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> select owner, f(owner) from stage; 72841 rows selected. ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs, userenv('client_info') from dual; CPU_HSECS USERENV('CLIENT_INFO') ---------- ---------------------------------------------------------- 64 32
  • 59. Scalar Subqueries ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time; dbms_application_info.set_client_info(0); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> select owner, f(owner) from stage; 72841 rows selected. ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs, userenv('client_info') from dual; CPU_HSECS USERENV('CLIENT_INFO') ---------- ----------------------------------------------------------- 69 0
  • 60. Scalar Subqueries ops$tkyte%ORA11GR2> exec :cpu := dbms_utility.get_cpu_time; dbms_application_info.set_client_info(0); PL/SQL procedure successfully completed. ops$tkyte%ORA11GR2> select owner, (select f(owner) from dual) from stage; 72841 rows selected. ops$tkyte%ORA11GR2> select dbms_utility.get_cpu_time-:cpu cpu_hsecs, user CPU_HSECS USERENV('CLIENT_INFO') ---------- -------------------------------------------------------------- 19 0