Skip to Main Content
  • Questions
  • JDBC : SQL vs PL/SQL, Which performs better

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question.

Asked: April 13, 2001 - 4:09 pm UTC

Last updated: November 28, 2006 - 11:57 am UTC

Version: 8.1.5

Viewed 10K+ times! This question is

You Asked

Hi Tom:
I have been asked to prove whether SQL performance is better than PL/SQL performance while being called from java. Following is my setup:
Table trans - 5 million recs, indexes on trans_id,area_code and zip_code
TRANS_ID NOT NULL NUMBER(12)
CUSTOMER_ID NUMBER(6)
AREA_CODE NUMBER(3)
PHONE_NUMBER NUMBER(7)
ZIP_CODE NUMBER(5)
ZIP_ADDL_CODE NUMBER(5)
TOTAL_MINS NUMBER(6)
DISCOUNT NUMBER(12,5)
TOTAL_AMOUNT NUMBER(12,5)
SESSION_ID NUMBER(16)

Table areamaster - 300 recs - index on area_code
Name Null? Type
AREA_CODE NOT NULL NUMBER(3)
RATE NUMBER(12,2)

Table zipmaster - 1000 recs - index on zip_code
ZIP_CODE NOT NULL NUMBER(5)
ZIP_ADDL_CODE NUMBER(5)
CITY_NAME VARCHAR2(30)
----------------------------------------------------------------------
java code for standard SQL (meat of it) is
try
{
Class.forName(dbDriver).newInstance();
Connection conn = DriverManager.getConnection(dbUrl,
dbUser, dbPassword);

String sql = "SELECT a.trans_id, a.customer_id, a.phone_number, b.rate,
" +
" c.city_name from areamaster b, zipmaster c, trans a" +
" where c.zip_code = a.zip_code and " +
" b.area_code = a.area_code and c.zip_code = ?" +
" order by 1,2 ";

PreparedStatement stmt = null;
startTime = System.currentTimeMillis();
System.out.println("Start Time: " + startTime);

for(int i=0;i<101;i++) {
stmt = conn.prepareStatement(sql);
stmt.setInt(1,i);
ResultSet rs = stmt.executeQuery();
rs.close();
stmt.close();
}
endTime = System.currentTimeMillis();
System.out.println("End Time: " + endTime);
timeElapsed = endTime - startTime;
System.out.println("Time Elapsed: " + timeElapsed);
System.out.println("******************************");
conn.close();
----------------------------------------------------------------------
java code for PL/SQL (meat of it) is
try
{
Class.forName(dbDriver).newInstance();
Connection conn = DriverManager.getConnection(dbUrl,
dbUser, dbPassword);

startTime = System.currentTimeMillis();
System.out.println("Start Time: " + startTime);
for(int i=0;i<101;i++) {
stmt = conn.prepareCall("{call s2_pack.s2_proc(?,?)}");
stmt.setInt(1,i);
stmt.registerOutParameter(2, OracleTypes.CURSOR);
stmt.execute();
rset = ((OracleCallableStatement) stmt).getCursor(2);
rset.close();
stmt.close();
}

endTime = System.currentTimeMillis();
System.out.println("End Time: " + endTime);
timeElapsed = endTime - startTime;
System.out.println("Time Elapsed: " + timeElapsed);
System.out.println("******************************");
System.out.println("End of test");
conn.close();
return;
}
----------------------------------------------------------------------
pl/sql package.proc being called above is (returning ref cursor)

create or replace package s2_pack is
type trans_rec is record
(
trans_id trans.trans_id%type,
customer_id trans.customer_id%type,
phone_number trans.phone_number%type,
rate areamaster.rate%type,
city_name zipmaster.city_name%type
);
type transrec_refcurtype is ref cursor return trans_rec;
procedure s2_proc(x_in_zip in number,
x_out_cursor in out transrec_refcurtype);
end;
/
create or replace package body s2_pack is
procedure s2_proc (x_in_zip in number,
x_out_cursor in out transrec_refcurtype)
as
begin
open x_out_cursor for
select a.trans_id,
a.customer_id,
a.phone_number,
b.rate,
c.city_name
from areamaster b,
zipmaster c,
trans a
where c.zip_code = a.zip_code and
b.area_code = a.area_code and
c.zip_code = x_in_zip
order by
1,2;
end s2_proc;
end s2_pack;
/
----------------------------------------------------------------------

QUESTION IS:

I am getting better performance with the java code calling the SQL
than the one calling the stored proc. Why is that ? The "select" statement in both is the same. I expect the PL/SQL SP one to perform better. Also, I noticed that a lot of time is spent in returning the ref cursor in the java code. Is my test invalid or incorrect ? If so, what is the best way to prove or test that SP's perform better than standard SQL while being called from java ? Please note that the above specific case returns 500,000 rows in each case i.e. 5000 rows per iteration and I ran the above as a single client session on a Unix box. More specifically the SQL one ended in 108437 milliseconds and the PL/SQL
one ended in 110535 milliseconds. I also analyzed all the tables and indexes and also did a dbms_shared_pool.keep on the procedure to pin it. Quite frankly, I am baffled - I must be doing something wrong.
Please advise !

Thanks in advance !






and Tom said...

#1. PL/SQL that just opens a ref cursor and returns it and does nothing could not possibly be faster then just SQL. I would expect them to be about the same. PL/SQL cannot magically make SQL go faster, you have not reduced the amount of data on the network, there are no "time based" efficiencies to be gained this way.

What you DO GAIN with this approach is:

- the owner of the procedure needs SELECT on the table, not the user connecting to the database via jdbc. They just need EXECUTE on the process. This is much more secure in my opinion. By putting all of the sql logic into the database, you avoid people connecting via SQL plus and mucking about with the data -- all they can do is run your stored procedures.

- it is infinitely easier to fix SQL bugs this way. Suppose you discover the query is wrong. Zap a stored procedure and instantly everyone is running the new code -- no downloads, no find all installations of the java and update the classes, no muss, no fuss. This is huge.

- it is infinitely easier to tune the SQL this way. I can run the stored procedure in SQL plus using sql_trace and tkprof (
</code> http://asktom.oracle.com/~tkyte/tkprof.html <code>
) and tune it outside of the application. I don't have to recompile the java classes, run them, tune the sql, recompile the java and so on. Just recreate a stored procedure and run.

- I can add new functionality at the drop of a hat. All of a sudden -- you need to track WHO is running that SQL for security purposes. I can tweak a stored procedure in seconds, put it out there and wah-lah -- my changes are in effect for EVERY user, even if they are already running the application.

and so on. The advantages of the stored procedure are in the management and flexibility of it all. There is absolutely no change that PLSQL executing your SQL can cause a select to return data faster (or slower)

You should use the stored procedure, it is just a better programming technique, is more secure and protects you from changes in the long run

#2. 108.437 seconds VERSUS 110.535 seconds shows the two are basicaly very comparable actually. You quite simply cannot draw any conclusions from this test -- it was a draw. Less then 2 additional WALL CLOCK seconds per 100 executions. 2/100th of a second (0.02). I would actually put that down to "noise". If you run the program a couple of dozen times - sometimes PLSQL would win, sometimes SQL would win (at least it does on my machine) by a second or two. It is not a meaningful difference in execution time.

#3. Your program is designed poorly from a performance standpoint and doesn't really test what you need to test (you never FETCH the data!). Your test case adds tons of overhead to the application by preparing the statement INSIDE of the loop. The entire concept of a callable statement is to remove the need to soft parse and setup the statement each and every time. That is like compiling a subroutine everytime you want to call it -- you would never consider doing that, so do not do it in sql.

I ran the test below, with these results:

$ java test
JDBC driver version is 8.1.6.2.0
2001-04-14 09:12:34.122:Going to PLSQL_RightWay
2001-04-14 09:14:50.014:Going to SQL_RightWay
2001-04-14 09:17:08.36:done

On this particular invocation, plsql took 135.892 seconds whereas sql took 138.346 seconds (on the prior run, SQL won by about 1.5 seconds, this time it lost by 2.5 seconds -- next time it might win again)


Here is the SQL for the test:


create or replace package my_pkg
as
type rc is ref cursor;
procedure open_cursor
( p_count in number, p_cursor in out rc );
end;
/


create or replace package body my_pkg
as
procedure open_cursor( p_count in number, p_cursor in out rc )
is
begin
open p_cursor
for select object_name
from all_objects
where rownum <= p_count;
end;
end;
/


And now the java:



import java.sql.*;
import oracle.jdbc.driver.*;
import oracle.sql.*;

class test
{

public static void showTimeStamp( String msg )
{
System.out.println( (new
Timestamp(System.currentTimeMillis())).toString() +
":" + msg );
}

static CallableStatement cstmt_persistent;

public static void PLSQL_RightWay(Connection conn ) throws Exception
{
if ( cstmt_persistent == null )
{
cstmt_persistent =
conn.prepareCall("{ call my_pkg.open_cursor( ?,? ) }" );
cstmt_persistent.registerOutParameter( 2, OracleTypes.CURSOR );
}

cstmt_persistent.setInt( 1, 5000 );
cstmt_persistent.execute();
ResultSet rset = (ResultSet)cstmt_persistent.getObject(2);
while( rset.next() )
{
//System.out.println( rset.getString(1) );
}
rset.close();
}

static PreparedStatement pstmt_persistent;

public static void SQL_RightWay(Connection conn ) throws Exception
{
if ( pstmt_persistent == null )
{
pstmt_persistent =
conn.prepareStatement
("select object_name from all_objects where rownum <= ?");
}

pstmt_persistent.setInt( 1, 5000 );

ResultSet rset = pstmt_persistent.executeQuery();
while( rset.next() )
{
//System.out.println( rset.getString(1) );
}
rset.close();
}

public static void main(String args[])throws Exception
{
DriverManager.registerDriver
(new oracle.jdbc.driver.OracleDriver());

Connection conn=DriverManager.getConnection
("jdbc:oracle:thin:@aria-dev:1521:ora816dev"
,"scott","tiger");

DatabaseMetaData meta=conn.getMetaData();
System.out.println
("JDBC driver version is "+meta.getDriverVersion());

conn.setAutoCommit(false);

// stmt.execute( "alter session set sql_trace=true" );

showTimeStamp( "Going to PLSQL_RightWay" );
for( int i = 0; i < 100; i++ ) PLSQL_RightWay(conn);

showTimeStamp( "Going to SQL_RightWay" );
for( int i = 0; i < 100; i++ ) SQL_RightWay(conn);

showTimeStamp( "done" );
}
}







Rating

  (44 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

What about pre-fetching?

Niranjan Ramakrishnan, September 10, 2001 - 10:52 pm UTC

Isn't it true that we can do ResultSet pre-fetching when executing SQL directly from JDBC but *can not* when getting a REF CURSOR from PL/SQL? Won't this mean additional round-trips for the latter which will make it even less performant?

Tom Kyte
September 10, 2001 - 11:55 pm UTC

That is absolutely NOT true. Consider:

create or replace package demo_pkg
as
type refcur is ref cursor;

procedure get_cur( x in out refcur );
end;
/

create or replace package body demo_pkg
as

procedure get_cur( x in out refcur )
is
begin
open x for select ename from emp;
end;

end;
/

And java:

import java.sql.*;
import java.io.*;
import oracle.jdbc.driver.*;


class curvar
{
public static void main (String args [])
throws SQLException, ClassNotFoundException
{
String query = "begin demo_pkg.get_cur( :1 ); end;";

DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());

Connection conn=
DriverManager.getConnection
("jdbc:oracle:thin:@aria-dev:1521:ora817dev",
"scott", "tiger");

Statement sqlTrace = conn.createStatement ();
sqlTrace.execute( "alter session set sql_trace=true" );
sqlTrace.close();

CallableStatement cstmt = conn.prepareCall(query);
cstmt.registerOutParameter(1,OracleTypes.CURSOR);
cstmt.execute();

ResultSet rset = (ResultSet)cstmt.getObject(1);

while( rset.next() )
System.out.println( rset.getString(1) );

rset.close();
cstmt.close();
}
}

The tkprof report shows us:

SELECT ENAME
FROM
EMP


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


That is, it took 2 fetches to get 14 rows showing the default prefetch of 10 was in action with this ref cursor.

In my case, the ad hoc SQL performs far better than the PL/SQL

Jon Atkinson, May 29, 2003 - 1:45 pm UTC

I'm doing something very similar, but the results aren't so close. The ad hoc SQL executes in about 300ms, while calling the function and iterating thru the results, even from sql*plus, takes over 2 seconds. The results are the same (300ms and > 2sec) when I execute the ad hoc SQL or the function from Java over the JDBC thin driver.

We added some debugging output to the Java code that showed that an inordinate amount of time is spent in oracle.jdbc.driver.OracleCallableStatement.getCursor( int ). Does that method just create the ref cursor, send it over the wire, and cast it to a Java ResultSet? Or does it also execute the SQL up to the first returned row?

The SQL is the same in both cases, except that the function uses parameterized variables in the SQL, instead of changing constants. Thus this approach is far more scalable because it's using bind variables. But overall it's no more scalable, because of the performance hit of the ref cursor.

We're using a function that returns a ref cursor, instead of a procedure that takes a ref cursor variable as an OUT parameter. I don't think this will make any difference, but I'll try it that way to be sure.

I can understand that the PL/SQL can't execute SQL faster than the SQL itself. But it seems like there is substantial overhead with ref cursors, and it's not caused by the JDBC thin driver or the network.

Are we missing something here? Are there other approaches (like returning a PL/SQL collection) that would verify that the ref cursor is the cause of the performance hit? If not, are there any workarounds to this?

Thanks for your time.

Tom Kyte
May 29, 2003 - 3:56 pm UTC

reproducible example please? You've got mine (did you run it? do you see the same thing)?

sql versus pl/sql - what about readability?

R Menon, June 05, 2003 - 10:45 am UTC

Thanx for a great web site &#8211; I am a big fan of yours!

You keep suggesting that if you can do it in sql, do it instead of using procedural (pl/sql) code &#8211; and I agree.

However, I am wondering whether the readability of code decreases once the select stmt becomes too complex and we start hitting a point of diminishing returns. For example, do you think the following query in the procedure (performance wise it is fine) should be rewritten in procedural code (which would do it step by step and presumably would be more readable)?

Is the burden on the developer to understand the query (it is reasonably simple if you break it in steps but it does take considerably more effort than procedural code )?
What are your thoughts?

---the example query --
procedure get_multi_hosts_comp_summary ( p_job_guid raw,
p_execution_guid raw, p_cursor out refCursor)
is
begin
open p_cursor for
select delta_comp_guid, lhs_host_name, rhs_host_name,
decode ( delta_comp_guid, null, 'BEING_COMPUTED',
decode ( instr ( hw_result||os_result || oracle_result ||
os_registered_sw_result, 'ERROR'), 0,
decode
( instr(hw_result||os_result || oracle_result ||
os_registered_sw_result, 'COW_RESULT_DIFFERENT'),
0,'COW_RESULT_SAME', 'COW_RESULT_DIFFERENT'
),
'ERROR_IN_COMPARISON'
)
) result,
decode( delta_comp_guid, null, 'BEING_COMPUTED',
decode( hw_result, 'ERROR', 'HW_COMPARISON_ERROR',
hw_result ) ) hw_result,
decode( delta_comp_guid, null, 'BEING_COMPUTED',
decode( os_result, 'ERROR', 'OS_COMPARISON_ERROR',
os_result ) ) os_result,
decode( delta_comp_guid, null, 'BEING_COMPUTED',
decode( oracle_result, 'ERROR', 'OS_COMPARISON_ERROR',
oracle_result ) ) oracle_result,
decode( delta_comp_guid, null, 'BEING_COMPUTED',
decode( os_registered_sw_result, 'ERROR', 'OS_COMPARISON_ERROR',
os_registered_sw_result ) ) os_registered_sw_result
from
(
select delta_comp_guid, lhs_host_name, rhs_host_name,
max(hw_result) hw_result,
max(os_result) os_result,
max(oracle_result) oracle_result,
max(os_registered_sw_result) os_registered_sw_result
from
(
with common_columns as
(
select a.job_guid job_guid, map.delta_comp_guid delta_comp_guid,
a.lhs_host_name lhs_host_name, a.rhs_host_name rhs_host_name
from
(
select distinct j.job_id job_guid, jp.scalar_value lhs_host_name,
t.target_name rhs_host_name
from mgmt_job j, mgmt_job_exec_summary exs,
mgmt_job_target jt, mgmt_targets t,
mgmt_job_parameter jp
where j.job_id = p_job_guid
and j.job_id = exs.job_id
and exs.execution_id = p_execution_guid
and jt.job_id = j.job_id
and jt.target_guid = t.target_guid
and jp.job_id = j.job_id
and jp.execution_id = exs.execution_id
and jp.parameter_name = 'firstCompareTarget'
) a,
mgmt_comp_id_to_job_id_map map
where a.job_guid = map.job_guid (+)
and a.rhs_host_name = map.rhs_host_name(+)
)
select c.delta_comp_guid, c.lhs_host_name, c.rhs_host_name,
decode(max(error_count), 0,
decode(max(s.different_count+s.left_count+s.right_count), 0,
'COW_RESULT_SAME', 'COW_RESULT_DIFFERENT'),
'ERROR'
) hw_result,
null os_result,
null oracle_result,
null os_registered_sw_result
from common_columns c, mgmt_delta_comp_summaries s
where c.delta_comp_guid = s.delta_comp_guid(+)
and instr(HOST_CONFIG_HW_VIEW_LIST, s.collection_type) != 0
group by c.delta_comp_guid, c.lhs_host_name, c.rhs_host_name
union
select c.delta_comp_guid, c.lhs_host_name, c.rhs_host_name,
null hw_result,
decode(max(error_count), 0,
decode(max(s.different_count+s.left_count+s.right_count), 0,
'COW_RESULT_SAME', 'COW_RESULT_DIFFERENT'),
'ERROR'
) os_result,
null oracle_result,
null os_registered_sw_result
from common_columns c, mgmt_delta_comp_summaries s
where c.delta_comp_guid = s.delta_comp_guid(+)
and instr('HOST_CONFIG_OS_VIEW_LIST', s.collection_type) != 0
group by c.delta_comp_guid, c.lhs_host_name, c.rhs_host_name
union
select c.delta_comp_guid, c.lhs_host_name, c.rhs_host_name,
null hw_result,
null os_result,
decode(max(error_count), 0,
decode(max(s.different_count+s.left_count+s.right_count), 0,
'COW_RESULT_SAME', 'COW_RESULT_DIFFERENT'),
'ERROR'
) oracle_result,
null os_registered_sw_result
from common_columns c, mgmt_delta_comp_summaries s
where c.delta_comp_guid = s.delta_comp_guid(+)
and instr(ORACLE_TOPLEVEL_VIEW_NAME, s.collection_type) != 0
group by c.delta_comp_guid, c.lhs_host_name, c.rhs_host_name
union
select c.delta_comp_guid, c.lhs_host_name, c.rhs_host_name,
null hw_result,
null os_result,
null oracle_result,
decode(max(error_count), 0,
decode(max(s.different_count+s.left_count+s.right_count), 0,
'COW_RESULT_SAME', 'COW_RESULT_DIFFERENT'),
'ERROR') os_registered_sw_result
from common_columns c, mgmt_delta_comp_summaries s
where c.delta_comp_guid = s.delta_comp_guid(+)
and instr('ECM$ORACLE_TOPLEVEL', s.collection_type) != 0
group by c.delta_comp_guid, c.lhs_host_name, c.rhs_host_name
) diff_qry
group by delta_comp_guid, lhs_host_name, rhs_host_name
) diff_qry1;

end;



Tom Kyte
June 05, 2003 - 1:19 pm UTC

i would never rewrite working, fast sql as procedural code.

Tell me -- how would a procedural implementation of that be "more readable" or understandable. (maybe comments on what it is doing would help -- but that would be as true for a procedural implementation wouldn't it)

JDBC : SQL vs PL/SQL, Which performs better

R Menon, June 05, 2003 - 3:10 pm UTC

well, since procedural code is done in steps
it is more readable than a huge sql select -
(kinda like a huge function versus one that is split
up into many) - at least that is my opinion.

On the same topic, from the book &#8220;Oracle Performance Planning&#8221;, we get:
"
When implementing data access (queries) or data
changes (DML), use SQL. This rule is specific to the business logic modules of code where procedural code is mixed with data access (non-procedural SQL)
code. There is great temptation to put procedural logic into the SQL access. This
tends to result in poor SQL that is resource-intensive. SQL statements with
DECODE case statements are very often candidates for optimization, as are
statements with a large amount of OR predicates or set operators, such as UNION
and MINUS."

Again this seems to suggest that we should not have huge
selects..



Tom Kyte
June 05, 2003 - 3:34 pm UTC

I disagree 100% -- but do what makes you feel "good" and performs "better then good enough".


I often take tons of procedural code and make it a single "resource intensive" sql statement.

How can fetching data out - to process in PLSQL (which is an interpreted language basically) that is running on the same server -- be better in general then doing it in SQL on that same server without a context switch -- in C?


That query you have would be broken into tons of just as hard to understand queries (at the aggregate level, looking at the procedure) -- even harder I would say. Do this -- write the procedural implementation of that and ask yourself "if I documented both equally well, which is 'better'"



agree with you

R Menon, June 05, 2003 - 4:39 pm UTC

hi tom
I will stick to the select statement way of doing
things (as per your suggestion) - you are probably
right- the procedural code also may be complex.
thanx!:)

Dave, June 05, 2003 - 6:09 pm UTC

It seems a truism to say o, but whether it's procedural code or SQL you make it easier to maintain by adding descriptive comments, right?

It seems too often that a perfectly rational developer can pepper PL/SQL with expplanatory comments but has some kind of a mental block on commenting within the sql itself.

Consider the complex SQL example given. What would be wrong with providing in-line commentary on the real meaning of some of the complex decodes, or the individual data sets that are union'ed together? I'd bet that if it was broken down into procedural code it would get commented. Or am I wrong?

Tom Kyte
June 05, 2003 - 6:52 pm UTC

I actually find that my SQL is more and more procedural when I look at it.  Especially with inline views.

I tend to build queries from the inside out -- they are like subroutines to me actually (oh, that might put a date on me -- let me call them "methods")

Here is a cool little "program" that creates a golfing schedule for 8 player over 7 weeks, randomly assigning them to different groups:

ops$tkyte@ORA920> with players as
  2  ( select 'P'||rownum username
  3      from all_objects
  4     where rownum <= 8),
  5  weeks as
  6  ( select rownum week
  7      from all_objects
  8     where rownum <= 7 )
  9  select week,
 10         max(decode(rn,1,username,null)) u1,
 11         max(decode(rn,2,username,null)) u2,
 12         max(decode(rn,3,username,null)) u3,
 13         max(decode(rn,4,username,null)) u4,
 14         max(decode(rn,5,username,null)) u5,
 15         max(decode(rn,6,username,null)) u6,
 16         max(decode(rn,7,username,null)) u7,
 17         max(decode(rn,8,username,null)) u8
 18   from ( select username,
 19                 week,
 20                 row_number() over (partition by week order by rnd) rn
 21            from ( select username, week, dbms_random.random rnd
 22                     from players, weeks
 23                 )
 24       )
 25   group by week
 26  /


      WEEK U1 U2 U3 U4 U5 U6 U7 U8
---------- -- -- -- -- -- -- -- --
         1 P5 P4 P7 P1 P3 P2 P6 P8
         2 P3 P6 P8 P7 P1 P5 P4 P2
         3 P8 P1 P7 P6 P5 P4 P3 P2
         4 P3 P5 P8 P4 P7 P2 P1 P6
         5 P8 P7 P1 P4 P3 P6 P5 P2
         6 P3 P2 P6 P1 P4 P8 P7 P5
         7 P1 P2 P4 P8 P7 P5 P6 P3

7 rows selected.


U1..U4 is one group, U5..U8 is another....  when asked to do this for a friend (come up with random assignments over 7 weeks for 8 players), this was actually the first approach that popped into my head...

comments might be:

o create a set of 7 weeks (weeks)

o create a set of 8 players (players)

o cartesian product weeks with players, so we end up with 56 rows -- each player
  in each week.  Assign Random number to each row in this set

o Now, for each week, sort the players by that random number -- and assign the
  number 1, 2, 3, 4, .. 8 to them after sorting (that is the row_number() )

o then use a standard pivot technique to pivot the results by week.


If you use that in a create table Assignments as <select>, you can use:

ops$tkyte@ORA920> with players as
  2  ( select 'P'||rownum username
  3      from all_objects
  4     where rownum <= 8)
  5  select u1,
  6        max( decode( rn, 1, decode(u2,u1,to_number(null),plays), null )) p1,
  7        max( decode( rn, 2, decode(u2,u1,to_number(null),plays), null )) p2,
  8        max( decode( rn, 3, decode(u2,u1,to_number(null),plays), null )) p3,
  9        max( decode( rn, 4, decode(u2,u1,to_number(null),plays), null )) p4,
 10        max( decode( rn, 5, decode(u2,u1,to_number(null),plays), null )) p5,
 11        max( decode( rn, 6, decode(u2,u1,to_number(null),plays), null )) p6,
 12        max( decode( rn, 7, decode(u2,u1,to_number(null),plays), null )) p7,
 13        max( decode( rn, 8, decode(u2,u1,to_number(null),plays), null )) p8
 14   from ( select u1, u2, plays,
 15                 row_number() over ( partition by u1 order by u2) rn
 16            from ( select a.username u1, b.username u2,
 17                         (select count(*)
 18                            from assignments
 19                           where (select a.username from dual) in (u1,u2,u3,u4)
 20                             AND (select b.username from dual) in (u1,u2,u3,u4)
 21                         ) +
 22                         (select count(*)
 23                            from assignments
 24                           where (select a.username from dual) in (u5,u6,u7,u8)
 25                             AND (select b.username from dual) in (u5,u6,u7,u8)
 26                         ) plays
 27                    from players a,
 28                         players b
 29                 )
 30        )
 31  group by u1
 32  /


U1  P1  P2  P3  P4  P5  P6  P7  P8
-- --- --- --- --- --- --- --- ---
P1       5   3   1   3   4   2   3
P2   5       1   3   3   4   2   3
P3   3   1       3   3   4   4   3
P4   1   3   3       3   2   6   3
P5   3   3   3   3       2   4   3
P6   4   4   4   2   2       1   4
P7   2   2   4   6   4   1       2
P8   3   3   3   3   3   4   2

8 rows selected.


to see how "fair" the assignments were -- that shows P1 plays with P2 5 times, P3, 3 times and so on.

I'll let you figure that one out -- it would obviously need comments in real code -- but I think you'll find it almost procedural in nature if you start from the inside and work your way out.
 

Agree with DaveJDBC : SQL vs PL/SQL, Which performs better

A reader, June 05, 2003 - 6:42 pm UTC

yes inline comments would make it much more readable.
would do that for my code - thanx!:)

Does whole PL/SQL get parsed only during the first invocation from java?

Tony Li, July 10, 2003 - 6:47 pm UTC

Since you are using bind variables when you invoke PL/SQL from Java side. I wonder if the whole PL/SQL gets parsed during the first invocation from java.

If this is true, does that mean that there is no need to use dynamic PL/SQL for parsing individual SQL statement in the PL/SQL package? Because the whole package has been parsed during the first invocation.

Thanks,

Tony

Tom Kyte
July 11, 2003 - 7:34 am UTC

plsql is stored compiled, so a plsql package/procedure/function is already parsed and compiled.

what gets parsed at runtime is your "begin p; end;" block -- not P, just that anonymous block.


Now, if you plsql routine does sql, that sql will be parsed as normal. an initial soft (probably) parse and then plsql caches that cursor so subsequent executions of it don't parse at all.

if you do dynamic sql, then the sql is parsed EVERY time you invoke it (unless you use dbms_sql and parse once, execute many).

using static sql is best, use dynamic sql only when static sql just won't work at all.

A reader, July 11, 2003 - 9:53 am UTC


My experiment just proved what you have told me

Tony Li, July 11, 2003 - 6:43 pm UTC

Thanks you so very much for your quick reply and wonderful advice! My experiment just proved your statement.

Many Thanks!!!

JDBC: comparing sql in Java versus sql in pl/sql

A reader, September 28, 2003 - 6:47 pm UTC

Hi Tom
I know you are in favor of moving sql statements
from Java to plsql for various reasons that you already
have mentioned. I was trying to set up a benchmark
to see if there were any performance related
differences between the two cases.

I was not expecting any performance differences.
Anyways, what puzzled me that if I have a code as:
create the callable statement:

for (int i=0; i < 10; i ++)
{
bind the callable statement
execute the callable statement (returns a ref cursor)
..
}
then the callable statement was getting soft parsed
10 times. I would have expected it to get parsed
only once. If I put the same select in Java
it got soft parsed only once (since I prepared
the statement only once across the iteration.)

Is there a way to ensure that the same can be
true for plsql case as well? Does this have something
to do with the fact that we are passing an
anonymous block to execute a procedure (there is
no other way ) I set the session_cached_cursors to
100 and still see the same behavior

Menon:)

Tom Kyte
September 28, 2003 - 7:57 pm UTC

ref cursors are soft parsed each time -- they have to be.

session_cached_cursor offsets this.

but really -- why would you be in a hard loop getting N result sets back into java when you could just call a procedure to process N result sets

ah! There I go again!

A reader, September 28, 2003 - 9:34 pm UTC

This is the second time - I forgot about the ref
cursors!!!
Anyways, the only reason I was looping was
to execute the same query again and again so that the
measurement times are big enough to average out.


I do agree - we will never need to do the looping
at the client side in real life. I did realize it
but as I said I was trying to get more reliable benchmark
results.

I guess I have to select a really big table
to do the benchmark then?


Thanx!
Menon:)



Tom Kyte
September 29, 2003 - 7:27 am UTC

depends on what exactly it is you are trying to measure.

If you are trying to measure the client performance -- it would be best to set up a test driver that called the client over and over and over with different implementations (one with straight sql, other with plsql calls)

JDBC: SQL PL/SQL

Zilani, September 29, 2003 - 3:19 am UTC

Hi tom
i am greatful for your site. I need a solution for inserting a collection from java to a table using PL/SQL procedure. I want to write a procedure to do this and call it from java. The parameter of the procedure may be refcursor/array or anything like that. I want to get the collection and want to insert by loop in the procedue. It will be helpfull if you show me an example.
best regards.

thanx!

A reader, September 29, 2003 - 10:55 am UTC

"depends on what exactly it is you are trying to measure."

Well, I was trying to measure the differences in
two cases:
1. I use a select statement in Java.
2. I use an anonymous block that calls a stored
procedure from Java doing the same thing that the
select statement does.

I didn't really expect any performance differences
in the two but wanted to benchmark it anyways.
I "solved" the issue by changing the ref cursor
issue in the select to return a count instead :)
But somehow my results are fairly random!
In some runs they are close to each other in
terms of run time and in terms of latch consumption.
In other runs the plsql case was around 50% slower!!
The database is newly installed (9.2.0.1.0)
Could you kindly take a look at the benchmarks and
see if there is something wrong? I use your
DbmsOutput.java and your runstats_pkg apart from
the following files.

Thanx a ton!
Menon:)

---schema.sql--
rop table t1;
create table t1 as
select sys_guid() key1, owner, object_id, object_type
from all_objects;

commit;
insert /*+APPEND */ into t1
select sys_guid() key1, 'SYS', object_id, object_type from all_objects;

commit;
insert /*+APPEND */ into t1
select sys_guid() key1, 'SYS', object_id, object_type from all_objects;

commit;
insert /*+APPEND */ into t1
select sys_guid() key1, 'SYS', object_id, object_type from all_objects;

alter table t1 add constraint t1_pk primary key ( key1 );
create index t1_idx1 on t1 ( owner );
analyze table t1 compute statistics;
------
------Benchmark.java - uses DbmsOutput.java and
------- your runstats package to compare
------- two approaches - effectively runs
-------your runstats from java and prints results

import java.sql.*;
import oracle.jdbc.OracleDriver;
import oracle.jdbc.OracleConnection;
import oracle.jdbc.OraclePreparedStatement;
import oracle.jdbc.OracleCallableStatement;

public class Benchmark
{
public static void setSessionCachedCursors ( OracleConnection connection,
int sessionCachedCursors )
throws SQLException
{
String stmtStr = "alter session set session_cached_cursors=" +
sessionCachedCursors ;
Statement stmt = null;
try
{
stmt = connection.createStatement();
stmt.execute( stmtStr );
}
finally
{
stmt.close();
}
}

public static void startTrace ( OracleConnection connection )
throws SQLException
{
String setTimedStatisticsStmt = "alter session set timed_statistics=true";
String setTraceStmt =
"alter session set events '10046 trace name context forever, level 12'";
Statement stmt = null;
try
{
stmt = connection.createStatement();
stmt.execute( setTimedStatisticsStmt );
stmt.execute( setTraceStmt );
}
finally
{
stmt.close();
}
}

public static void markStart( OracleConnection connection )
throws SQLException
{
_benchmarkStatementArray[BENCHMARK_START_INDEX].execute();
}

public static void markMiddle( OracleConnection connection )
throws SQLException
{
_benchmarkStatementArray[BENCHMARK_MIDDLE_INDEX].execute();
}

public static void markEnd( OracleConnection connection )
throws SQLException
{
_benchmarkStatementArray[BENCHMARK_STOP_INDEX].execute();
}

public static void closeBenchmarkStatements (
OracleConnection connection ) throws SQLException
{
for( int i=0; i < _benchmarkStatementArray.length; i++)
{
_benchmarkStatementArray[i].close();
}
}

public static void prepareBenchmarkStatements (
OracleConnection connection ) throws SQLException
{
_benchmarkStatementArray[BENCHMARK_START_INDEX]=
(OracleCallableStatement) connection.prepareCall( BENCHMARK_START );

_benchmarkStatementArray[BENCHMARK_MIDDLE_INDEX]=
(OracleCallableStatement) connection.prepareCall( BENCHMARK_MIDDLE );

_benchmarkStatementArray[BENCHMARK_STOP_INDEX]=
(OracleCallableStatement) connection.prepareCall( BENCHMARK_STOP );
_benchmarkStatementArray[BENCHMARK_STOP_INDEX].setInt(1,
BENCHMARK_DIFFERENCE_THRESHOLD);
_dbmsOutput = new DbmsOutput ( connection );
_dbmsOutput.enable ( DBMS_OUTPUT_BUFFER_SIZE );
}

public static void printBenchmarkResults()
throws SQLException
{
_dbmsOutput.show();
_dbmsOutput.close();
}

private static String BENCHMARK_START = "begin runstats_pkg.rs_start; end;";
private static String BENCHMARK_MIDDLE = "begin runstats_pkg.rs_middle; end;";
private static String BENCHMARK_STOP = "begin runstats_pkg.rs_stop(?); end;";

private static OracleCallableStatement[] _benchmarkStatementArray = new OracleCallableStatement[3];

private static DbmsOutput _dbmsOutput;
private static final int DBMS_OUTPUT_BUFFER_SIZE = 1000000;

private static final int BENCHMARK_START_INDEX = 0;
private static final int BENCHMARK_MIDDLE_INDEX = 1;
private static final int BENCHMARK_STOP_INDEX = 2;
private static final int BENCHMARK_DIFFERENCE_THRESHOLD = 200;
}
----------TestSqlInJavaOrPlsql.java -----
import java.sql.*;
import oracle.jdbc.OracleDriver;
import oracle.jdbc.OracleConnection;
import oracle.jdbc.OraclePreparedStatement;
import oracle.jdbc.OracleCallableStatement;

public class TestSqlInJavaOrPlsql
{

public static void main(String[] args) throws SQLException
{
long startTime=0, middleTime=0, endTime=0;

DriverManager.registerDriver( new OracleDriver());
_checkUsage( args );

OracleConnection connection = _getOracleConnection();
Benchmark.setSessionCachedCursors ( connection, 100 );
Benchmark.prepareBenchmarkStatements ( connection );

_prepareJavaBasedSqlStmt( connection );
_preparePlsqlBasedSqlStmt( connection );

// first execute through Java based statement
try
{
Benchmark.startTrace( connection );
//startTime = System.currentTimeMillis();
Benchmark.markStart ( connection );
_executePlsqlBasedCode ( connection );

//middleTime = System.currentTimeMillis();
Benchmark.markMiddle ( connection );
_executeJavaBasedCode ( connection );
Benchmark.markEnd ( connection );
//endTime = System.currentTimeMillis();

long timeTakenForJava = (middleTime - startTime);
long timeTakenForPlsql = (endTime - middleTime);
Benchmark.printBenchmarkResults ();
}
finally
{
_closeAll( connection );
}
}

private static void _closeAll ( OracleConnection connection )
{
try
{
_javaBasedStmt.close();
_plsqlBasedStmt.close();
Benchmark.closeBenchmarkStatements ( connection );
connection.close();
}
catch ( SQLException e)
{
//ignore;
}
}

private static void _checkUsage (String[] args)
{
int argc = args.length;
if( argc > 1)
{
System.out.println( "usage: java TestSqlInJavaOrPlsql num_of_iterations");
System.out.flush();
System.exit(1);
}

if (argc == 1)
{
_numOfIterations = Integer.parseInt( args[0] );
}
}

private static OracleConnection _getOracleConnection ()
throws SQLException
{
String connectionURL = "jdbc:oracle:thin:@usunrat24:1521:ora92i";
OracleConnection connection = (OracleConnection)
DriverManager.getConnection ( connectionURL, "benchmark", "benchmark");

connection.setAutoCommit ( false) ;
return connection;
}

private static void _prepareJavaBasedSqlStmt( OracleConnection connection )
throws SQLException
{
String javaBasedSQLStmtString =
"select /* from java */ count(*) from t1 " +
" where owner = ?";
_javaBasedStmt = (OraclePreparedStatement)
connection.prepareStatement ( javaBasedSQLStmtString );
}

private static void _preparePlsqlBasedSqlStmt( OracleConnection connection )
throws SQLException
{
String plsqlBasedSQLStmtString = "begin ? := test_pkg.get_count_data(?); end;";
_plsqlBasedStmt = (OracleCallableStatement)
connection.prepareCall ( plsqlBasedSQLStmtString );
}

private static void _executeJavaBasedCode( OracleConnection connection)
throws SQLException
{
ResultSet rset = null;
int count = 0;
try
{
for( int i=0; i < BENCHMARK_ITERATION_COUNT; i++)
{
_javaBasedStmt.setString(1, "SYS");

rset = _javaBasedStmt.executeQuery();
int j=0;
if ( rset.next() )
{
count = rset.getInt(1);
}
if( i == 0)
System.out.println ( "Java : result" + count );
}
}
finally
{
if ( rset != null )
{
try
{
rset.close();
}
catch (Exception e) {} // ignore;
}
}

}

private static void _executePlsqlBasedCode( OracleConnection connection)
throws SQLException
{
int count = 0;
try
{
for( int i=0; i < BENCHMARK_ITERATION_COUNT; i++)
{
_plsqlBasedStmt.setString( 2, "SYS" );
_plsqlBasedStmt.registerOutParameter( 1, oracle.jdbc.OracleTypes.NUMBER);
_plsqlBasedStmt.execute();
count = ( int) _plsqlBasedStmt.getInt(1);
if( i == 0)
System.out.println ( "Plsql : result" + count );
}
}
finally
{
;
/*
if ( rset != null )
{
try
{
rset.close();
}
catch (Exception e) {} // ignore;
}
*/
}

}

private static OracleCallableStatement _plsqlBasedStmt;
private static OraclePreparedStatement _javaBasedStmt;
private static int _numOfIterations = 1;
private static final int BENCHMARK_ITERATION_COUNT = 200;
}
-----




Tom Kyte
September 29, 2003 - 12:50 pm UTC

way too much code for me to read, especially written in a foreign language like that.

You run it lots.
You average the results.

sorry - forgot one benchmark file

Menon, September 29, 2003 - 10:57 am UTC

---test_pkg.sql
create or replace package test_pkg as
function get_data ( p_owner_name in varchar2 )
return sys_refcursor;
function get_count_data ( p_owner_name in varchar2 )
return number;
end;
/
show errors
/
create or replace package body test_pkg as
function get_data ( p_owner_name in varchar2 )
return sys_refcursor
is
l_cursor sys_refcursor;
begin
open l_cursor for
select /* from plsql */ owner, object_id, object_type
from t1
where owner = p_owner_name;

return l_cursor;
end;

function get_count_data ( p_owner_name in varchar2 )
return number
is
l_count number;
begin
select /* from plsql */ count(*)
into l_count
from t1
where owner = p_owner_name;

return l_count;
end;
end;
/
show errors
/
----
Menon:)

Broken Link

Shree 420, September 29, 2003 - 11:46 am UTC

Hi Tom,
This link you posted is broken :-(
</code> http://asktom.oracle.com/~tkyte/tkprof.html <code>
Is it possible for you to fix it?
That will really help. Thank you.

Tom Kyte
September 29, 2003 - 12:52 pm UTC

fixed

RE: Broken Link

Mark A. Williams, September 29, 2003 - 12:56 pm UTC

The problem is the page that is linked to redirects to a non-existant page. But, really, how hard would it be to look up the documentation with having the link?

- Mark

thanx Tom!

Menon, September 29, 2003 - 1:10 pm UTC

"way too much code for me to read, especially written in a foreign language like
that. "

No problems - I understand - if I find anything amiss
I will post it here. If anyone wants to take a crack
at reviewing the code - you are more than welcome:)

Menon:)

RE: Crack at the code

Mark A. Williams, September 29, 2003 - 1:22 pm UTC

Well, I don't think I will take a crack at the code, but I can say that I just read Cary Millsap's new book (Optimizing Oracle Performance) over the weekend and I highly, highly recommend it for doing performance investigation. It gets very technical and detailed and is at points rigorous (which is what makes it good), especially the chapter on Queueing Theory, but it is important stuff nonetheless IMO. (Not that my opinion should carry much weight mind you)...

- Mark

Tom Kyte
September 29, 2003 - 2:17 pm UTC



it is a really cool book, i'm enjoying it. He put into words so eloquently things I've been trying to say -- I keep finding myself saying "yeah, you go" while I'm reading it.

thanx Mark!

Menon, September 29, 2003 - 1:44 pm UTC

"Well, I don't think I will take a crack at the code, but I can say that I just
read Cary Millsap's new book (Optimizing Oracle Performance) over the weekend
and I highly, highly recommend it for doing performance investigation. It ..."

You read it over the weekend?!:) That is amazing - it
must be really good. Well, mine is on its way already!
I hope to utilize it in my own product soon.

Thanx anyways:)
Menon:)



great book!

Menon, October 03, 2003 - 10:21 am UTC

I have not read it fully yet - but it is really an awesome
book! He has taken the usage of sql trace to the next
level and has given scientific proof for all his statements. Once you read it, it all seems common sense
and that is the sign of a truely great writer!

Tom - do you think his method will become the defacto
performance diagnotic method instead of the ususal
method being followed currently?

Thanx!

Tom Kyte
October 03, 2003 - 10:59 am UTC

it already is, isn't it?

given that the "current method" does not really work....

that is true !

Menon, October 03, 2003 - 12:30 pm UTC

but I think his idea of narrowing constraining
the data diagnotic collection process accurately and
also his rigor of focusing on the business objective
is not being followed currently in majority of tuning
projects. This also comes back to your idea of instrumenting your code. Lots of similarities between
your approach and his approach.

Menon:)

Thank

Zilani, October 07, 2003 - 3:35 am UTC

Hi Tom
Thanks for your reply.Its great! Your online help is most useful.
Best regards

Steve, November 11, 2003 - 11:07 am UTC

Tom, you wrote above,
"i would never rewrite working, fast sql as procedural code."

I definitely agree, however it seems that Steven Feuerstein tends to disagree on this topic.

See the "2. Break Your SQL Addiction" section at:
</code> https://asktom.oracle.com/Misc/oramag/on-constraints-case-and-cursors.html <code>

I'm curious to hear your thoughts on this.
Thanks.

Tom Kyte
November 11, 2003 - 12:04 pm UTC

we have to agree to disagree. it won't be the first time.

If you flip a couple more pages to my article in asktom, same magazine, you'll see my counter point to that!

different strokes for different folks. I believe he is a procedural programmer.

I consider myself a database developer.





"Break your SQL addiction"

Tony Andrews, November 11, 2003 - 12:33 pm UTC

In this article, SF is advocating a "modularised" approach to using SQL. Maybe Tom would agree with the purpose but not with the methods?

For instance, the example he gives is using a function to hide the expression last_name||','||first_name, to protect the application from requirements changes such as using a space instead of a comma.

An alternative would be to create a view including:

..., last_name||','||first_name as full_name, ...

That achieves the same objective without invoking PL/SQL.

Tom Kyte
November 11, 2003 - 1:44 pm UTC


his approach is somewhat orthogonal to what I propose in my article ('where does a cursor belong')

You can definitely "over modularize". A view would be much more appropriate then layers and layers of code.

I do agree with many things there -- like keeping the code to the size that it fits on the screen!!!

WITH AS

A reader, March 04, 2004 - 7:14 pm UTC

Tom, I've never seen above type of SQL could you please refer me to a document where I can find more information regarding this or perhaps can you explain?
I mean the [WITH player AS (select ...] part of it. It looks as a new 9i feature.

Thanks


Tom Kyte
March 04, 2004 - 7:45 pm UTC

search for


"subquery factoring"


in quotes on this site for more info

Fantastic

Raaghid, May 31, 2004 - 4:23 pm UTC

This is what I am looking for. Calling stored procedure from JSP.

Gr8.... help.....

question on commented sql

A reader, June 18, 2004 - 7:08 pm UTC

hi tom
Are the sql comments
retained in the "top sql" that one gets from
statspack?

thanx!

Tom Kyte
June 19, 2004 - 8:02 am UTC

yes.

Where do you close the connection object?

Fred, May 25, 2005 - 9:12 pm UTC

In your Java sample above (in response to the second or third posting), it doesn't look like you close the db connection object. Where are you doing this? The Java mantra is to ensure that we close this or else we run the risk of hosing the system.

Tom Kyte
May 26, 2005 - 7:38 am UTC

exit a program and the connection is closed, but it was just a demo of a ref cursor, not a real program.

it should not 'hose' the system to not close a connection as the act of a program terminating will do that.

CallableStatement declared "static"

Matthias Schulz, May 22, 2006 - 7:15 am UTC

Hello Tom,

you use static declarations of the CallableStatement in several examples througout AskTom and I think I remember that you mentioned somewhere that this helps to reduce parsing.

I just wonder if this works on a J2EE application server as well. Will the static declaration cause any problems when used several times in parallel with different variables binded to it?

Best regards,
Matthias Schulz

P.S.: Your seminar in Munich was fabulous!

Tom Kyte
May 22, 2006 - 8:01 am UTC

If the code is truly "called in parallel", global variables are "dangerous". Is the code really called "in parallel" so that the same "state" would be used by all?

CallableStatement declared "static"

Matthias Schulz, May 22, 2006 - 8:53 am UTC

Hello Tom,

thank you for your quick reply.

The idea is to put all SQL in the database as PL/SQL procedures and call these from the J2EE server with CallableStatement's.
So to me there is a big chance that some of these calls will be processed n parallel.
I am especially worried about the ones that deliver opened reference cursors, because there will be more than one reference cursor used by the same java class opened at the same time.


Tom Kyte
May 22, 2006 - 3:52 pm UTC

"define parallel" please.

parallel as in "we have 50 sqlplus sessions hitting the enter key simultaneously"

or parallel as in "we are in a multithreaded application"

CallableStatement declared "static"

Matthias Schulz, May 22, 2006 - 8:27 pm UTC

Hello Tom,

what I tried to express with "parallel" is my concern about what will happen, when I declare a CallableStatement as static in a java class and:

1) several clients call the same class at the same time (could be 2 could be 2500 instances of that java class; all with different bind variable values; hostest on up to four J2EE-Servers running in parallel)

2) client A uses a reference cursor that was delivered by an instance of java class "OraRefCursor" and now client B creates an instance of the same class with different bind variable values and tries to use his delivered cursor too.

In short: Is it dangerous to declare CallableStatement or PreparedStatement as static in a java class, when I can't exclude (real) parallel use of that class (meaning several instances created and used simultanously)?

And if the aswers is yes, what is the next best alternative? Declare CallableStatement or PreparedStatement not as static, but as private?

Thank you very much in advance.

Best regards
Matthias Schulz

Tom Kyte
May 23, 2006 - 7:27 am UTC

1) 2500 instances of that class - great, every *instance* has it's own state.

2) as long as it is rebound, perfect.


Do you have a java programmer around? They would be useful here in explaining how it works



sounds OK from what you have described thus far however.

CallableStatement declared "static" - doesn't work !?

Matthias Schulz, June 27, 2006 - 8:07 am UTC

Hello Tom,

sorry for the delay, but here is an example that doesn't work with the PreparedStatement declared as static, but works well when the PreparedStatement is not declared static:

package jdbcprepstmt;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

import oracle.jdbc.pool.OracleDataSource;

public class OraclePrepStmt{
String jobquery;
static PreparedStatement prepStmt; //

public OraclePrepStmt(Connection conn) throws SQLException {
jobquery = "SELECT OWNER, OBJECT_TYPE, OBJECT_NAME FROM ALL_OBJECTS WHERE OWNER = upper(?) AND OBJECT_TYPE = upper(?)";
prepStmt = conn.prepareStatement(jobquery);
}


public void setSelectObjectOwner(String objectOwner) throws SQLException{
prepStmt.setString(1, objectOwner);
}


public void setSelectObjectType(String objectType) throws SQLException{
prepStmt.setString(2, objectType);
}


public void printAllObjects() throws SQLException {
// get the result set
ResultSet rset = prepStmt.executeQuery();

// determine the number of columns in each row of the result set
ResultSetMetaData rsetMeta = rset.getMetaData();
int count = rsetMeta.getColumnCount();

// print the results, all the columns in each row
int rows = 0;
while (rset.next()) {
rows++;
String rsetRow = " ";
for (int i = 1; i <= count; i++) {
rsetRow = rsetRow + " - " + rset.getString(i);
}
System.out.println(rsetRow);
}

// close the result set
rset.close();
}


public static void main(String[] args)throws SQLException {
// connect to a local XE database
OracleDataSource ods = new OracleDataSource();
ods.setURL("jdbc:oracle:thin:tech/tech@localhost:1521/XE");
Connection conn = ods.getConnection();
conn.setAutoCommit(false);

// use the prepared statement class
OraclePrepStmt ps1 = new OraclePrepStmt(conn);
ps1.setSelectObjectOwner("SYS");
ps1.setSelectObjectType("TABLE");

OraclePrepStmt ps2 = new OraclePrepStmt(conn);
ps2.setSelectObjectOwner("SYSTEM");
ps2.setSelectObjectType("VIEW");

OraclePrepStmt ps3 = new OraclePrepStmt(conn);
ps3.setSelectObjectOwner("SYSTEM");
ps3.setSelectObjectType("TABLE");

ps1.printAllObjects();
ps2.printAllObjects();
ps3.printAllObjects();
ps1.printAllObjects();


// close database connections
conn.close();
ods.close();
}
}
------------------------------------------------------------
Here is the result with the PreparedStatement declared as static (which is obviously wrong):
- SYSTEM - TABLE - DEF$_TEMP$LOB
- SYSTEM - TABLE - OL$
- SYSTEM - TABLE - OL$HINTS
- SYSTEM - TABLE - OL$NODES
- SYSTEM - TABLE - HELP
- SYSTEM - TABLE - DEF$_TEMP$LOB
- SYSTEM - TABLE - OL$
- SYSTEM - TABLE - OL$HINTS
- SYSTEM - TABLE - OL$NODES
- SYSTEM - TABLE - HELP
- SYSTEM - TABLE - DEF$_TEMP$LOB
- SYSTEM - TABLE - OL$
- SYSTEM - TABLE - OL$HINTS
- SYSTEM - TABLE - OL$NODES
- SYSTEM - TABLE - HELP
- SYSTEM - TABLE - DEF$_TEMP$LOB
- SYSTEM - TABLE - OL$
- SYSTEM - TABLE - OL$HINTS
- SYSTEM - TABLE - OL$NODES
- SYSTEM - TABLE - HELP

------------------------------------------------------------
Here is the result with the PreparedStatement not declared as static (which works as desired):
- SYS - TABLE - DUAL
- SYS - TABLE - SYSTEM_PRIVILEGE_MAP
- SYS - TABLE - TABLE_PRIVILEGE_MAP
- SYS - TABLE - STMT_AUDIT_OPTION_MAP
- SYS - TABLE - WRI$_ADV_ASA_RECO_DATA
- SYS - TABLE - KU$NOEXP_TAB
- SYS - TABLE - IMPDP_STATS
- SYS - TABLE - PLAN_TABLE$
- SYS - TABLE - AUDIT_ACTIONS
- SYS - TABLE - PSTUBTBL
- SYS - TABLE - ODCI_SECOBJ$
- SYS - TABLE - ODCI_WARNINGS$
- SYSTEM - VIEW - PRODUCT_PRIVS
- SYSTEM - TABLE - DEF$_TEMP$LOB
- SYSTEM - TABLE - OL$
- SYSTEM - TABLE - OL$HINTS
- SYSTEM - TABLE - OL$NODES
- SYSTEM - TABLE - HELP
- SYS - TABLE - DUAL
- SYS - TABLE - SYSTEM_PRIVILEGE_MAP
- SYS - TABLE - TABLE_PRIVILEGE_MAP
- SYS - TABLE - STMT_AUDIT_OPTION_MAP
- SYS - TABLE - WRI$_ADV_ASA_RECO_DATA
- SYS - TABLE - KU$NOEXP_TAB
- SYS - TABLE - IMPDP_STATS
- SYS - TABLE - PLAN_TABLE$
- SYS - TABLE - AUDIT_ACTIONS
- SYS - TABLE - PSTUBTBL
- SYS - TABLE - ODCI_SECOBJ$
- SYS - TABLE - ODCI_WARNINGS$
------------------------------------------------------------

So, this begs the question:
How to declare the PreparedStatement right and performant?

Thank you for your answer,
best regards,

Matthias Schulz

Tom Kyte
June 27, 2006 - 9:27 am UTC

do you know what static does to a variable in Java?

In your case, where you "new" them like that - you don't want static as that creates ONE variable for all instances.

JDBC : SQL vs PL/SQL, Which performs better

A reader, July 10, 2006 - 8:57 am UTC

I have been reading the posts regarding SQL vs. PL/SQL performance as a part of tuning some queries that are used as in-line SQL in a Java frontend and I am somewhat confused. I need to develop some recommendations about how we should be doing queries in our Java frontend, both from a performance POV, security POV and best practices POV. And I need to be able to prove those recommendations to our Java developers primarily in terms of performance timings as that seems to carry more weight than either the security or best practices POVs. I took one of my re-written queries and ran it as both a SQL query and a procedure that could return the data to the Java frontend. Initially, I assumed that the PL/SQL would be faster but from reading the posts it appears that the SQL is faster. But when I ran my test case the PL/SQL procedure is faster and would appear to scale much better according to Runstats. I have included the TKPROF output and Runstats comparison. This is a 10g system, I was the only user for my testing of the whole process. I flushed the shared pool before turning on tracing and ran the SQL query twice and then the procedure. I then exited tracing, re-opened SQL Plus and ran the Runstats. There are a number of things that were puzzling me from this. I have bolded what I think is the relevant output in the TKPROF. My questions are as follows:

1. There were many queries that appeared in the TKPROF that I removed so things could fit in this post that did not seem relevant. What are they and what, if anything, do they signify?
2. I actually ran the SQL query twice so that the trace would reflect both the first time and a subsequent run but that does not show up in the TKPROF output. Am I missing something or did I do something wrong?
3. The security predicate does not show up in the TKPROF output for the PL/SQL procedure. Is it just buried or is there something wrong?
4. It appears the PL/SQL does better all around on this. Does PL/SQL perform better for something like this or was this just a fluke? Should I do this again? The Runstats comparison seems incredible.
5. Should I be concerned about the “SQL net message from client” results particularly for the PL/SQL procedure?
6. We are transitioning to using Hibernate to execute our Oracle queries. Can Hibernate run an Oracle stored procedure and take a result set from it?

SQL Query

select priv, priv_class, a.sid, a.type_code, a.subtype_code,
a.title, a.subtype_desc, a.create_by, a.create_by_name, a.create_date,
a.status_desc, a.last_update_date, a.dtg
from dciis_owner.activity_access_vw_lisa a where 1 = 1 and status in ('OP', 'CL', 'DR');

PL/SQL Procedure Code

declare
p_data dciis_owner.myActivityTableType;
begin
dciis_owner.pass_activity_new(p_data);
end;

create or replace procedure pass_activity_new (p_data out myActivityTableType)
as
l_data myActivityTableType;
cursor c_data
is
select myActivityType( priv, priv_class, sid, type_code, subtype_code, title,
subtype_desc, create_by, create_by_name, create_date,
status_desc, last_update_date, dtg )
from dciis_owner.activity_access_vw_lisa
where 1=1 and status in ('OP', 'CL', 'DR');
begin
open c_data;
loop
fetch c_data bulk collect into l_data limit 100;
dbms_output.put_line ('Looping, c_data%rowcount = ' || c_data%rowcount);
exit when c_data%notfound;
end loop;
dbms_output.put_line('out of loop, c_data%rowcount = ' || c_data%rowcount);
close c_data;
end;

Output

TKPROF: Release 10.1.0.2.0 - Production on Thu Jun 29 07:30:28 2006

Copyright (c) 1982, 2004, Oracle. All rights reserved.

Trace file: prod24_ora_3390.trc
Sort options: default
.
.
.
begin :con := DCIIS_TABLE_SECURITY.SELECT_ACT_VW_LISA_PREDICATE(:sn, :on);
end;


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 6 0.00 0.00 0 0 0 0
Execute 6 0.04 0.02 0 0 0 6
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 12 0.04 0.03 0 0 0 6

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 77 (recursive depth: 1)
********************************************************************************
.
.
.
SELECT ROLE_CODE
FROM
ALL_ASSIGNMENT_ROLE_TYPE WHERE FORM_TYPE = :B3 AND TYPE_CODE = :B2 AND
(SUBTYPE_CODE = :B1 OR :B1 IS NULL)


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 6 0.04 0.03 0 0 0 0
Fetch 6 0.00 0.00 0 12 0 36
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 13 0.04 0.03 0 12 0 36

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 77 (recursive depth: 3)

Rows Row Source Operation
------- ---------------------------------------------------
36 INDEX RANGE SCAN ALL_ASSIROLETYPE_UK (cr=12 pr=0 pw=0 time=572 us)(object id 34198)

********************************************************************************
.
.
.
select priv, priv_class, a.sid, a.type_code, a.subtype_code,
a.title, a.subtype_desc, a.create_by, a.create_by_name, a.create_date,
a.status_desc, a.last_update_date, a.dtg
from dciis_owner.activity_access_vw_lisa a where 1 = 1 and status in ('OP', 'CL', 'DR')

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 2 0.37 0.38 0 94 0 0
Execute 2 0.00 0.00 0 0 0 0
Fetch 578 3.02 2.91 101 103624 0 8640
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 582 3.39 3.30 101 103718 0 8640

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 11515

Rows Row Source Operation
------- ---------------------------------------------------
4320 SORT AGGREGATE (cr=12960 pr=97 pw=0 time=261014 us)
4319 FIRST ROW (cr=12960 pr=97 pw=0 time=232877 us)
4319 INDEX RANGE SCAN (MIN/MAX) ALLUPDAHIST_ACTITIME_IDX (cr=12960 pr=97 pw=0 time=221992 us)(object id 149019)
4320 FILTER (cr=38852 pr=4 pw=0 time=1038748 us)
4771 HASH JOIN (cr=30722 pr=0 pw=0 time=972930 us)
4771 NESTED LOOPS (cr=24169 pr=0 pw=0 time=274941 us)
4771 NESTED LOOPS (cr=9735 pr=0 pw=0 time=160420 us)
4771 HASH JOIN (cr=193 pr=0 pw=0 time=50648 us)
3 INLIST ITERATOR (cr=9 pr=0 pw=0 time=343 us)
3 TABLE ACCESS BY INDEX ROWID REF_CODES (cr=9 pr=0 pw=0 time=291 us)
3 INDEX RANGE SCAN REFCODE_UK (cr=6 pr=0 pw=0 time=131 us)(object id 33456)
4771 TABLE ACCESS FULL ACTIVITY (cr=184 pr=0 pw=0 time=24074 us)
4771 TABLE ACCESS BY INDEX ROWID ACTIVITY_TYPE (cr=9542 pr=0 pw=0 time=100853 us)
4771 INDEX UNIQUE SCAN ACTITYPE_PK (cr=4771 pr=0 pw=0 time=56653 us)(object id 34062)
4771 TABLE ACCESS BY INDEX ROWID PERSONNEL (cr=14434 pr=0 pw=0 time=104237 us)
4771 INDEX UNIQUE SCAN PERS_PK (cr=9542 pr=0 pw=0 time=64216 us)(object id 34122)
4780 VIEW (cr=6553 pr=0 pw=0 time=639027 us)
4780 HASH GROUP BY (cr=6553 pr=0 pw=0 time=624631 us)
130997 TABLE ACCESS FULL ALL_ASSIGNMENTS (cr=6553 pr=0 pw=0 time=131257 us)
0 INLIST ITERATOR (cr=8130 pr=4 pw=0 time=89441 us)
0 TABLE ACCESS BY INDEX ROWID ALL_ASSIGNMENTS (cr=8130 pr=4 pw=0 time=79208 us)
0 INDEX RANGE SCAN ALLASSI_ACTIPERSASSIROL_INDX (cr=8130 pr=4 pw=0 time=61763 us)(object id 81151)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 578 0.00 0.00
SQL*Net message from client 578 3.36 1063.14
SQL*Net more data to client 42 0.00 0.00
db file sequential read 25 0.01 0.01
db file scattered read 23 0.00 0.00
********************************************************************************

select audit$,properties
from
type_misc$ where obj#=:1


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 4 0.00 0.00 0 0 0 0
Execute 4 0.00 0.00 0 0 0 0
Fetch 4 0.00 0.00 0 12 0 4
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 12 0.00 0.00 0 12 0 4

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: CHOOSE
Parsing user id: SYS (recursive depth: 1)

Rows Row Source Operation
------- ---------------------------------------------------
1 TABLE ACCESS CLUSTER TYPE_MISC$ (cr=3 pr=0 pw=0 time=82 us)
1 INDEX UNIQUE SCAN I_OBJ# (cr=2 pr=0 pw=0 time=33 us)(object id 3)

********************************************************************************

select privilege#,nvl(col#,0),max(mod(nvl(option$,0),2))
from
objauth$ where obj#=:1 and grantee#=:2 group by privilege#,nvl(col#,0)


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.00 0 0 0 0
Execute 6 0.00 0.00 0 0 0 0
Fetch 8 0.00 0.00 1 14 0 2
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 15 0.01 0.01 1 14 0 2

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: CHOOSE
Parsing user id: SYS (recursive depth: 1)

Rows Row Source Operation
------- ---------------------------------------------------
0 SORT GROUP BY (cr=2 pr=1 pw=0 time=6472 us)
0 TABLE ACCESS BY INDEX ROWID OBJAUTH$ (cr=2 pr=1 pw=0 time=6388 us)
0 INDEX RANGE SCAN I_OBJAUTH2 (cr=2 pr=1 pw=0 time=6375 us)(object id 109)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
db file sequential read 1 0.00 0.00
********************************************************************************

select obj#
from
oid$ where user#=:1 and oid$=:2


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 2 0.00 0.00 0 0 0 0
Execute 7 0.01 0.00 0 0 0 0
Fetch 7 0.00 0.00 1 15 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 16 0.01 0.01 1 15 0 1

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: CHOOSE
Parsing user id: SYS (recursive depth: 1)

Rows Row Source Operation
------- ---------------------------------------------------
0 TABLE ACCESS BY INDEX ROWID OID$ (cr=2 pr=0 pw=0 time=50 us)
0 INDEX UNIQUE SCAN I_OID1 (cr=2 pr=0 pw=0 time=38 us)(object id 294)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
db file sequential read 3 0.00 0.01
********************************************************************************

declare
p_data dciis_owner.myActivityTableType;
begin
dciis_owner.pass_activity_new(p_data);
end;

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.03 0.03 2 21 0 0
Execute 1 0.02 0.03 1 18 0 1
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 0.05 0.07 3 39 0 1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 11515

Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 1 0.00 0.00
SQL*Net message from client 1 0.00 0.00
********************************************************************************

SELECT MYACTIVITYTYPE( PRIV, PRIV_CLASS, SID, TYPE_CODE, SUBTYPE_CODE, TITLE,
SUBTYPE_DESC, CREATE_BY, CREATE_BY_NAME, CREATE_DATE, STATUS_DESC,
LAST_UPDATE_DATE, DTG )
FROM
DCIIS_OWNER.ACTIVITY_ACCESS_VW_LISA WHERE 1=1 AND STATUS IN ('OP', 'CL',
'DR')


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.18 0.19 2 90 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 44 1.47 1.42 0 51806 0 4320
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 46 1.65 1.62 2 51896 0 4320

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 7988 (recursive depth: 1)

Rows Row Source Operation
------- ---------------------------------------------------
4320 SORT AGGREGATE (cr=12960 pr=0 pw=0 time=190073 us)
4319 FIRST ROW (cr=12960 pr=0 pw=0 time=164356 us)
4319 INDEX RANGE SCAN (MIN/MAX) ALLUPDAHIST_ACTITIME_IDX (cr=12960 pr=0 pw=0 time=154077 us)(object id 149019)
4320 FILTER (cr=38852 pr=0 pw=0 time=980659 us)
4771 HASH JOIN (cr=30722 pr=0 pw=0 time=948046 us)
4771 NESTED LOOPS (cr=24169 pr=0 pw=0 time=278844 us)
4771 NESTED LOOPS (cr=9735 pr=0 pw=0 time=159559 us)
4771 HASH JOIN (cr=193 pr=0 pw=0 time=45034 us)
3 INLIST ITERATOR (cr=9 pr=0 pw=0 time=283 us)
3 TABLE ACCESS BY INDEX ROWID REF_CODES (cr=9 pr=0 pw=0 time=243 us)
3 INDEX RANGE SCAN REFCODE_UK (cr=6 pr=0 pw=0 time=118 us)(object id 33456)
4771 TABLE ACCESS FULL ACTIVITY (cr=184 pr=0 pw=0 time=24021 us)
4771 TABLE ACCESS BY INDEX ROWID ACTIVITY_TYPE (cr=9542 pr=0 pw=0 time=103931 us)
4771 INDEX UNIQUE SCAN ACTITYPE_PK (cr=4771 pr=0 pw=0 time=58000 us)(object id 34062)
4771 TABLE ACCESS BY INDEX ROWID PERSONNEL (cr=14434 pr=0 pw=0 time=108091 us)
4771 INDEX UNIQUE SCAN PERS_PK (cr=9542 pr=0 pw=0 time=66178 us)(object id 34122)
4780 VIEW (cr=6553 pr=0 pw=0 time=617665 us)
4780 HASH GROUP BY (cr=6553 pr=0 pw=0 time=603262 us)
130997 TABLE ACCESS FULL ALL_ASSIGNMENTS (cr=6553 pr=0 pw=0 time=131215 us)
0 INLIST ITERATOR (cr=8130 pr=0 pw=0 time=85286 us)
0 TABLE ACCESS BY INDEX ROWID ALL_ASSIGNMENTS (cr=8130 pr=0 pw=0 time=75542 us)
0 INDEX RANGE SCAN ALLASSI_ACTIPERSASSIROL_INDX (cr=8130 pr=0 pw=0 time=59257 us)(object id 81151)




********************************************************************************

OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 7 0.40 0.43 2 115 0 0
Execute 9 0.11 0.13 1 321 0 6
Fetch 578 3.02 2.91 101 103624 0 8640
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 594 3.53 3.47 104 104060 0 8646

Misses in library cache during parse: 4
Misses in library cache during execute: 3

Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 585 0.00 0.00
SQL*Net message from client 585 381.54 1503.92
SQL*Net more data to client 42 0.00 0.00
db file sequential read 25 0.01 0.01
db file scattered read 23 0.00 0.00


OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 238 0.29 0.28 2 90 0 0
Execute 744 0.45 0.43 0 0 0 6
Fetch 1946 1.59 1.58 4 55649 0 8120
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2928 2.33 2.30 6 55739 0 8126

Misses in library cache during parse: 41
Misses in library cache during execute: 40
.
.
.
Runstats

Run1 ran in 53422 hsecs
Run2 ran in 2236 hsecs
Run1 ran in 2389.18 % of the time

Name Run1 Run2 Diff
STAT...session uga memory 65,408 72,896 7,488
LATCH.enqueues 10,378 334 -10,044
LATCH.enqueue hash chains 11,845 334 -11,511
LATCH.SQL memory manager worka 13,312 554 -12,758
LATCH.library cache lock 15,397 383 -15,014
LATCH.library cache pin 35,561 675 -34,886
LATCH.shared pool 39,023 595 -38,428
STAT...session pga memory 114,016 65,536 -48,480
STAT...Elapsed Time 53,441 2,243 -51,198
LATCH.row cache objects 60,637 4,684 -55,953
STAT...physical read bytes 57,344 0 -57,344
STAT...physical read total byt 57,344 0 -57,344
LATCH.library cache 60,496 1,210 -59,286
LATCH.cache buffers chains 187,386 84,614 -102,772
LATCH.session allocation 243,919 3,479 -240,440
STAT...bytes sent via SQL*Net 410,334 1,773 -408,561
STAT...session pga memory max 1,621,344 262,144 -1,359,200
STAT...session uga memory max 1,484,312 65,568 -1,418,744

Run1 latches total versus runs -- difference and pct
Run1 Run2 Diff Pct
717,290 99,522 -617,768 720.74%

PL/SQL procedure successfully completed.


Tom Kyte
July 10, 2006 - 9:04 am UTC

Here is my advice:

stuff as much as you can into plsql.

If the data is read, processed and put back into the table (sort of like your exmaple, you didn't really DO anything with the data) - plsql will rock and roll. PLSQL is great for data processing.


If the data is read, formatted and displayed on screen - then return a ref cursor from PLSQL to java - let java call plsql, send inputs and have plsql return a cursor that the java program can fetch from and do whatever.

Security - belongs in the server (virtual private database, whatever). Because remember - applications come, applications go, the data however lives forever. This will not be the last application accessing this data - the next one might not even be in java! It might be written in an infinitely superior language. Or a proprietary language for a specialize bit of functionality. Or whatever.

JAVA slower than SQLPLus calling the same PL/SQL proc

Nancy, July 19, 2006 - 12:06 pm UTC

I see that from Java calling sqlplus and pl/sql is about the same. If you are making a call to a stored procedure that returns a row set 1) from slqplus client 2) from java client what kinds of factors would cause the java client to be twice as slow as the sqlplus client -

Would the hand off from the database to the JDBC layer just inherently be slower? (talking 800ms versus 1600ms) here.

I apologize for the "genericity" of my question - so a generic followup is fine.

Tom Kyte
July 20, 2006 - 7:51 am UTC

if you run your "benchmark" in a loop 1000 times, what is the average response. Java varies wildly that way in my experience.

but seeing your benchmark is always useful in trying to explain what you are observing. (so we can run it ourself)

Java

A Reader, July 26, 2006 - 3:00 pm UTC

Hi Tom

Could you please point out the Oracle features ( Including object relational ) that could be used to improve performance of web based application using Java.

Regards

Tom Kyte
July 26, 2006 - 4:07 pm UTC

Sure:

write less java
write more SQL
move data logic into PLSQL

it is usually that simple ;)



JDBC Methods

Arindam Mukherjee, November 28, 2006 - 3:26 am UTC

Respected Mr. Tom,

It’s my sincere request to have a look at the following snippet of code and please notice the last two lines. On reading, “Oracle9i - JDBC Developer’s Guide and Reference -Release 2 (9.2) (Part No A96654-01)”, I could not understand when I should use “executeUpdate()” and when “execute()”. I get the same results and presume both have same effect. Just to be sanguine, I ask you if there is any subtle difference between two methods. Please respond to.



String sql = "INSERT INTO " + tabName + " VALUES (?, ?)";

PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, empNo);
pstmt.setString(2, empName);

pstmt.executeUpdate();
pstmt.execute();


Regards,
Arindam Mukherjee


Tom Kyte
November 28, 2006 - 7:24 am UTC

what does the javadoc have to say (not really a java programmer see....)

(not really sure the use of the word sanguine is correct there


Main Entry: 1san·guine
Pronunciation: 'sa[ng]-gw&n
Function: adjective
Etymology: Middle English sanguin, from Anglo-French, from Latin sanguineus, from sanguin-, sanguis
1 : BLOODRED
2 a : consisting of or relating to blood b : BLOODTHIRSTY, SANGUINARY c of the complexion : RUDDY
3 : having blood as the predominating bodily humor; also : having the bodily conformation and temperament held characteristic of such predominance and marked by sturdiness, high color, and cheerfulness
4 : CONFIDENT, OPTIMISTIC

Remark on JDBC methods

Stew Ashton, November 28, 2006 - 8:48 am UTC

executeUpdate() is specifically intended for INSERT, UPDATE or DELETE statements. When appropriate, it returns the row count from the INSERT, UPDATE or DELETE. execute() is a general-purpose method that can be used with any SQL statement, so it will "work" anytime executeUpdate() does.
</code> http://java.sun.com/j2se/1.3/docs/api/java/sql/PreparedStatement.html <code>

Tom Kyte
November 28, 2006 - 9:36 am UTC

thanks!

context switch

Oded Erner, November 28, 2006 - 9:13 am UTC

Hi Tom,
We have a consultant who advices to use direct SQL instead of PL/SQL as we don’t context switch as it happens with PL/SQL(Going from PL/SQL engine to SQL engine).

The main SQL task is to update/insert/delete in bulks.

Our developers are coding PRO*C not JDBC but it seems to
be of the same case.

I will appreciate your answer,
Thanks very much, Oded.



Connection through JDBC

Arindam Mukherjee, November 28, 2006 - 9:56 am UTC

Stew Ashton from Paris, France, - Please accept my heartfelt thanks.

Mr. Tom, In connection with this, I have one question on Oracle Session. When I connect oracle database from java program uploaded to oracle database through “loadjava” by the following syntax,

// Connect to the database
Connection conn = DriverManager.getConnection("jdbc:default:connection:");

Any DML statements executed by Java Stored procedure, waits for “commit” explicitly.

If I change the connection like below, any DML statement in Java Stored procedures gets committed.

// connect to Oracle using JDBC driver

OracleDataSource ods = new OracleDataSource();
String url = "jdbc:oracle:thin:@// 10.114.57.37/orcl";
ods.setURL(url);
ods.setUser("scott");
ods.setPassword("tiger");
Connection conn = ods.getConnection();


Could you kindly explain me – why? Does this new connection open a new session and before closing this new connection, it gets committed.


Tom Kyte
November 28, 2006 - 11:57 am UTC

because jdbc the "enterprise api" uses the silly ODBC technique of "autocommit" by default.

the first line of all real jdbc applications after connect should be to DISABLE THE AUTOCOMMIT 'feature'

RE: Connection through JDBC

A reader, November 28, 2006 - 10:21 am UTC

Every time you do getConnection() using the JDBC thin driver, add this line:

conn.setAutoCommit(false);

As Tom keeps telling us, being in auto-commit mode is BAD, but it is the default so you have to turn it off after every getConnection().

</code> http://java.sun.com/j2se/1.3/docs/api/java/sql/Connection.html <code>

A reader, October 10, 2007 - 10:38 am UTC

import java.sql.*;
public class JDBCExample {
public static void main(String args[]) throws SQLException {
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
Connection conn=
DriverManager.getConnection("jdbc:oracle:thin:@dlsun137:5521:sol1",
" scott", "tiger");
Statement stmt = conn.createStatement();
ResultSet rs =
stmt.executeQuery("select ename, empno, sal from emp");
while(rs.next()){
String name= rs.getString(1);
int number = rs.getInt(2);
double salary = rs.getDouble(3);
System.out.println(name+" "+number+" "+salary);
}
rs.close();
conn.close();
}
}


i recieve the following error:-

JDBCExample.java:4: package oracle.jdbc.driver does not exist
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
^

regards,

jdbc

A reader, February 22, 2010 - 11:03 pm UTC


Testing SQL vs PL/ SQL Performance

Sanjaya Balasuriya, November 20, 2013 - 11:57 pm UTC

Hi Tom,

I created a simple test scenario to test SQL vs PL/SQL performance.

I have a simple procedure;

create or replace procedure sp1
as
vn1 number;
begin
select count(*) into vn1
from tab3;
end sp1;

Tab3 is;
create table tab3 as select * from tab;

And I capture timing with the following SQL script.

set serveroutput on;
alter system flush shared_pool;
alter system flush buffer_cache;
select to_char(systimestamp, 'dd-mm-yyyy hh24:mi:ss.ff6') from dual;
exec sp1;
select to_char(systimestamp, 'dd-mm-yyyy hh24:mi:ss.ff6') from dual;
alter system flush shared_pool;
alter system flush buffer_cache;
select to_char(systimestamp, 'dd-mm-yyyy hh24:mi:ss.ff6') from dual;
select sysdate from dual;
select count(*) from tab3;
select to_char(systimestamp, 'dd-mm-yyyy hh24:mi:ss.ff6') from dual;

In this test, time difference of before and after the stored procedure execution is consistently much lesser than the time difference of SQL execution.

Where would be the overhead for SQL execution? Can it be just network overhead?

Thanks and Regards,

Sanjaya

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library