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

Home -> Community -> Usenet -> c.d.o.server -> Re: Straight SQL always put perform PL/SQL?

Re: Straight SQL always put perform PL/SQL?

From: <fitzjarrell_at_cox.net>
Date: Fri, 21 Dec 2007 07:02:26 -0800 (PST)
Message-ID: <ded649d7-dc24-42fb-b7f5-b2fee8d5e752@r60g2000hsc.googlegroups.com>


On Dec 21, 5:15 am, "Jonathan Lewis" <jonat..._at_jlcomp.demon.co.uk> wrote:
> <dshprope..._at_gmail.com> wrote in message
>
> news:3f8191db-8372-4407-844a-1427b56a43f7_at_q77g2000hsh.googlegroups.com...
>
>
>
>
>
> > Thanks very much for your respond.  I read the "write consistency' as
> > well.  I think it is the opertimizer somehow messed up, the cursor
> > knows to use the index, while update statement choose a full table
> > scan on a big table.
>
> > To demonstrate it, I re-create the simplified situation of our case.
> > =====================================
> > create table test (id1 number,id2 number,id3 char(1), col1 char(2000))
> > pctfree 90;
> > (this is to mimic creating a huge table  which makes full table scan
> > expensive)
> > declare
> > v_number number :=1;
> > begin
> > loop
> > insert into test values (v_number, v_number + 1,'n','a');
> > v_number := v_number+1;
> > exit when v_number > 1000000;
> > end loop;
> > commit;
> > end;
>
> > create table test1 as select * from test where id1 < 400000;
> > update test1 set col1='b';
> > update test set id3='Y' where id1 < 500;
> > commit;
>
> > create unique index test_ind on test(id1,id2);
> > create bitmap index test_idx3 on test(id3);
> > create unique index test1_ind on test1(id1,id2);
>
> > exec dbms_stats.gather_table_stats('scott', 'TEST', method_opt=>'for
> > all indexed
> > columns', cascade=>TRUE);
> > exec dbms_stats.gather_table_stats('scott', 'TEST1', method_opt=>'for
> > all indexed
> > columns', cascade=>TRUE);
>
> > SQL> declare
> >  2    v_count number;
> >  3  CURSOR test_cur IS
> >  4  select test.id1,test.id2,test.col1 from test, test1
> >  5  where test.id1=test1.id1
> >  6  and test.id2 = test1.id2 and test.id3='Y';
> >  7  BEGIN
> >  8  FOR  test_curs IN test_cur
> >  9    LOOP
> > 10       update test
> > 11       set col1 = test_curs.col1
> > 12       where test.id1 = test_curs.id1
> > 13         and test.id2 = test_curs.id2
> > 14         and test.id3='Y';
> > 15     END LOOP;
> > 16  commit;
> > 17  END;
> > 18  /
>
> > PL/SQL procedure successfully completed.
>
> > Elapsed: 00:00:00.20
>
> > SQL> update test a
> >  2  set a.col1 = (select col1
> >  3  from test1 b
> >  4  where a.id1=b.id1
> >  5  and a.id2 = b.id2 and a.id3='Y')
> >  6  where exists
> >  7  (select 1 from test1 b where a.id1=b.id1
> >  8  and a.id2 = b.id2 and a.id3='Y');
>
> > 499 rows updated.
> > Elapsed: 00:01:27.75
>
> > PL/SQL is much faster.
>
> > Thanks very much for your help.
>
> I note that you didn't try rewriting the query as an update join view,
> which is still the fairest comparison with rewriting the PL/SQL to
> use a cursor loop driven by a join.
>
> However, it might help if you wrote the right query for your new
> example:
>
> update test a
>  set a.col1 = (
>   select col1
>   from test1 b
>   where b.id1 = a.id1
>   and b.id2 = a.id2
>  )
> where a.id3 = 'Y'
> and exists (
>   select 1
>   from test1 b
>   where b.id1 = a.id1
>   and b.id2 = a.id2
>  )
> ;
>
> I think you want to update TEST (the large table) where id3 = 'Y'.
> You want to update it with the matching value from TEST1 (the
> small table) where id1 and id2 match.
>
> Your version of the query put the "test.id3 = 'Y'" predicate in the
> subqueries - which made it impossible for Oracle to unnest the
> where clause subquery, or transform the query into a semi join.
>
> The join view update would look like this:
> update
>  (
>  select
>   a.col1 cola, b.col1 colb
>  from
>   test a, test1 b
>  where
>   a.id1 = b.id1
>  and a.id2 = b.id2
>  and a.id3 = 'Y'
>  ) v
> set cola = colb
> ;
>
> --
> Regards
>
> Jonathan Lewishttp://jonathanlewis.wordpress.com
>
> Author: Cost Based Oracle: Fundamentalshttp://www.jlcomp.demon.co.uk/cbo_book/ind_book.html
>
> The Co-operative Oracle Users' FAQhttp://www.jlcomp.demon.co.uk/faq/ind_faq.html- Hide quoted text -
>
> - Show quoted text -

Showing the pertinent details from using my last attempt at rewriting   your query and Jonathan's re-write we see:

SQL> declare
  2 v_count number;
  3 CURSOR test_cur IS
  4 select test1.id1,test1.id2,test1.col1 from test, test1   5 where test1.id1=test.id1
  6 and test1.id2 = test.id2 and test.id3='Y';   7 BEGIN
  8 FOR test_curs IN test_cur
  9 LOOP

 10       update test
 11       set col1 = test_curs.col1
 12       where test.id1 = test_curs.id1
 13         and test.id2 = test_curs.id2
 14         and test.id3='Y';
 15     END LOOP;

 16 commit;
 17 END;
 18 /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.46

SQL>
SQL> update test a
  2 set a.col1 = (select col1

  3          from test1 b
  4          where a.id1=b.id1
  5          and a.id2 = b.id2 and a.id3='Y')
  6 where (a.id1, a.id2) in
  7 (select b.id1, b.id2 from test1 b where a.id1=b.id1   8 and a.id2 = b.id2 and a.id3='Y');

499 rows updated.

Elapsed: 00:00:00.10

Execution Plan



Plan hash value: 3792759269
| Id  | Operation                      | Name      | Rows  | Bytes |
Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT               |           |   295 | 35990 |
180   (2)| 00:00:03 |
|   1 |  UPDATE                        | TEST      |       |
|            |          |
|   2 |   NESTED LOOPS                 |           |   295 | 35990 |
180   (2)| 00:00:03 |
|   3 |    TABLE ACCESS BY INDEX ROWID | TEST      |   744 | 83328
|    68   (3)| 00:00:01 |
|   4 |     BITMAP CONVERSION TO ROWIDS|           |       |
|            |          |
|*  5 |      BITMAP INDEX SINGLE VALUE | TEST_IDX3 |       |
|            |          |
|*  6 |    INDEX UNIQUE SCAN           | TEST1_IND |     1 |    10
|     1   (0)| 00:00:01 |
|*  7 |   FILTER                       |           |       |
|            |          |
|   8 |    TABLE ACCESS BY INDEX ROWID | TEST1     |     1 |   112
|     1   (0)| 00:00:01 |
|*  9 |     INDEX UNIQUE SCAN          | TEST1_IND |     1 |
|     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):


   5 - access("A"."ID3"='Y')
   6 - access("A"."ID1"="B"."ID1" AND "A"."ID2"="B"."ID2")
   7 - filter(:B1='Y')
   9 - access("B"."ID1"=:B1 AND "B"."ID2"=:B2)


Statistics


          1  recursive calls
        103  db block gets
       2582  consistent gets
          0  physical reads
     162800  redo size
        396  bytes sent via SQL*Net to client
        505  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
        499  rows processed

SQL>
SQL> update test a
  2 set a.col1 = (

  3            select col1
  4            from test1 b
  5            where b.id1 = a.id1
  6            and b.id2 = a.id2
  7           )
  8          where a.id3 = 'Y'
  9          and exists (
 10                    select 1
 11                    from test1 b
 12                    where b.id1 = a.id1
 13                    and b.id2 = a.id2
 14                   )
 15                  ;

499 rows updated.

Elapsed: 00:00:00.09

Execution Plan



Plan hash value: 2348765137
| Id  | Operation                      | Name      | Rows  | Bytes |
Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT               |           |     1 |   122 |
180   (2)| 00:00:03 |
|   1 |  UPDATE                        | TEST      |       |
|            |          |
|   2 |   NESTED LOOPS SEMI            |           |     1 |   122 |
180   (2)| 00:00:03 |
|   3 |    TABLE ACCESS BY INDEX ROWID | TEST      |   744 | 83328
|    68   (3)| 00:00:01 |
|   4 |     BITMAP CONVERSION TO ROWIDS|           |       |
|            |          |
|*  5 |      BITMAP INDEX SINGLE VALUE | TEST_IDX3 |       |
|            |          |
|*  6 |    INDEX UNIQUE SCAN           | TEST1_IND |     1 |    10
|     1   (0)| 00:00:01 |
|   7 |   TABLE ACCESS BY INDEX ROWID  | TEST1     |     1 |   112
|     1   (0)| 00:00:01 |
|*  8 |    INDEX UNIQUE SCAN           | TEST1_IND |     1 |
|     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):


   5 - access("A"."ID3"='Y')
   6 - access("B"."ID1"="A"."ID1" AND "B"."ID2"="A"."ID2")
   8 - access("B"."ID1"=:B1 AND "B"."ID2"=:B2)


Statistics


          1  recursive calls
        101  db block gets
       2582  consistent gets
          0  physical reads
     157824  redo size
        397  bytes sent via SQL*Net to client
        514  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
        499  rows processed

SQL> So SQL is faster than PL/SQL, when you write it correctly.

Thanks, Jonathan, for providing the correct query.

David Fitzjarrell Received on Fri Dec 21 2007 - 09:02:26 CST

Original text of this message

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