Showing posts with label PreparedStatement. Show all posts
Showing posts with label PreparedStatement. Show all posts

Saturday, February 1, 2014

Session Cached Cursors and JDBC PreparedStatement

Given two pseudo codes which would be better in terms of performance?


Case 1VsCase 2
Create PreparedStatement
  For Loop
    bind values
    execute
  End Loop
Close PreparedStatement
For Loop
  Create PreparedStatemnt
  bind values
  execute
  Close PreparedStatement
End Loop

From a developer's perspective it could be answered that first code only creates one preparedstatement and uses is multiple times and close it once whereas the second one creates create and close a preparedstatement with each iteration incurring additional overhead as such first code is better. What about the oracle perspective? would there be multiple parses on the second code? will it incur additional database resources? Looking at the trkprof output will have the following for these codes (java code used is given at the end of the post)
Case 1VsCase 2
call     count  
------- ------  
Parse        1  
Execute   1000  
Fetch     1000  
------- ------  
total     2001
call     count
------- ------
Parse     1000
Execute   1000
Fetch     1000
------- ------
total     3000

Tkprof output shows second code resulted in 1000 parses while first only had one parse so again first code must be better(?). The answer is yes the first set of code is better but not because it does less parses than the second code. If session cached cursors parameter is set to a value other than 0 (default 50) and cursor (or sql in the preparedstatement) is cached then the number of actual parses are exactly the same for both set of codes. Any performance loss on the second code set is due to session cache look up not due to parsing.
Cursor access could be ordered in following way when it comes to how fast they could be accessed.
1. Finding an open cursor in the session cache. Does not require parsing, not even soft.
2. Finding a close cursor in the session cache. Does not require parsing, not event soft.
3. Finding the cursor in the shared pool. Results in soft parse.
4. Constructing the cursor from scratch. Results in hard parse.
For more detail information refer Oracle performance tuning student guide Chapter 9, section cursor usage and parsing.
The java code used for testing is given at the end of the post. In the java code SQL1 denotes the "business" SQL. SQL2 is used to get the session statistics (total and hard parses, number of session cached cursors and session cache hits). SQL3 list all the open cursors in the session. Java code checks out a single connection from the UCP and execute the entire test case before closing it. Therefore statistics could be calculated comparing values at the time of connection checkout and at the time of connection close.

First set of test cases are run with session cached cursors parameters set to 50 (default). Run the java code several times until the hard parse count is 0 when connection is taken from the connection pool. Test begins from this point onwards. Executing the test case 1 (comment the code related to case 2 in the java code) will give an output similar to below.
start got connection
1 select /*+ connect_by_filtering */ privilege#,level from sys   DICTIONARY LOOKUP CURSOR CACHED   SYS    34
2 insert into sys.aud$( sessionid,entryid,statement,ntimestamp   OPEN-RECURSIVE                    ASANGA 34
3 select value$ from props$ where name = 'GLOBAL_DB_NAME'        DICTIONARY LOOKUP CURSOR CACHED   SYS    34
4 select SYS_CONTEXT('USERENV', 'SERVER_HOST'), SYS_CONTEXT('U   DICTIONARY LOOKUP CURSOR CACHED   SYS    34
5 select decode(failover_method, NULL, 0 , 'BASIC', 1, 'PRECON   DICTIONARY LOOKUP CURSOR CACHED   SYS    34
6 select privilege# from sysauth$ where (grantee#=:1 or grante   DICTIONARY LOOKUP CURSOR CACHED   SYS    34
7 select rownum,sql_text,cursor_type,USER_NAME,sid from v$open   OPEN                              ASANGA 34

Parse_Total Hard_Parse Ses_Cur_Cache_Hit Ses_Cur_Cache_Count CPU
        9         0         0                 7               5
end got connection

start execute
1 select * from (SELECT name,  value FROM v$sesstat,  v$statna   DICTIONARY LOOKUP CURSOR CACHED   ASANGA 34
2 select /*+ connect_by_filtering */ privilege#,level from sys   DICTIONARY LOOKUP CURSOR CACHED   SYS    34
3 insert into sys.aud$( sessionid,entryid,statement,ntimestamp   OPEN-RECURSIVE                    ASANGA 34
4 select value$ from props$ where name = 'GLOBAL_DB_NAME'        DICTIONARY LOOKUP CURSOR CACHED   SYS    34
5 select SYS_CONTEXT('USERENV', 'SERVER_HOST'), SYS_CONTEXT('U   DICTIONARY LOOKUP CURSOR CACHED   SYS    34
6 select decode(failover_method, NULL, 0 , 'BASIC', 1, 'PRECON   DICTIONARY LOOKUP CURSOR CACHED   SYS    34
7 select privilege# from sysauth$ where (grantee#=:1 or grante   DICTIONARY LOOKUP CURSOR CACHED   SYS    34
8 select b from x where a = :1                                   DICTIONARY LOOKUP CURSOR CACHED   ASANGA 34
9 select rownum,sql_text,cursor_type,USER_NAME,sid from v$open   DICTIONARY LOOKUP CURSOR CACHED   ASANGA 34

Parse_Total Hard_Parse Ses_Cur_Cache_Hit Ses_Cur_Cache_Count CPU
        10         0         2                 9              93
end execute
Output of SQL3 list the row number, sql text of the cursor, cursor type and the username and finally the SID. It's interesting that Oracle reference doc says for v$open_cursor the username refers to "User that is logged in to the session" still some cursors list username as sys while the session id is of current session which is of user asanga. The second output (from SQL2) list total parses, hard parses, session cache cursor hits and session cache cursor count and CPU used by the session.
Looking at the output at the time of the connection checkout the session (or JDBC connection) had done 9 parses (soft) as there are 0 hard parses listed. It has 7 cursors in it's session cache and no session cache cursor hits and so far has used 5 CPU centi-seconds. The 7 cursors and their types are also listed. What's missing from this initial output is the SQL2 as it has not been run when the session cursor content was listed. But once the SQL2 is executed and cursor is closed that too will be added as a session cached cursor.
After executing the test case the total parse count is increased to 10 but as there are no hard parses , the additional parse is a soft parse. However during this period at least 3 different SQLs were executed (SQL1,SQL2 and SQL3) and SQL1 had multiple executions as well. Yet only one soft parse has resulted during this time. There are two session cache cursor hits, these are as a result of running SQL2 and SQL3 as they had already been cached. So the additional parse is for SQL1 and even though it was run 10000 times it resulted only in one soft parse (as show in the trace file output listed earlier). This shows that finding an open cursor in the session does not result in soft parses when the cursor is used for multiple executions. Total CPU usage for the test case 1 is 88 (93-5).
To run the test case 2 uncomment the case 2 section and comment the case 1 section. Output for this test case is shown below.
start got connection
1 select /*+ connect_by_filtering */ privilege#,level from sys        DICTIONARY LOOKUP CURSOR CACHED   SYS    34
2 insert into sys.aud$( sessionid,entryid,statement,ntimestamp        OPEN-RECURSIVE                    ASANGA 34
3 select value$ from props$ where name = 'GLOBAL_DB_NAME'             DICTIONARY LOOKUP CURSOR CACHED   SYS    34
4 select SYS_CONTEXT('USERENV', 'SERVER_HOST'), SYS_CONTEXT('U        DICTIONARY LOOKUP CURSOR CACHED   SYS    34
5 select decode(failover_method, NULL, 0 , 'BASIC', 1, 'PRECON        DICTIONARY LOOKUP CURSOR CACHED   SYS    34
6 select privilege# from sysauth$ where (grantee#=:1 or grante        DICTIONARY LOOKUP CURSOR CACHED   SYS    34
7 select rownum,sql_text,cursor_type,USER_NAME,sid from v$open        OPEN                              ASANGA 34

Parse_Total Hard_Parse Ses_Cur_Cache_Hit Ses_Cur_Cache_Count CPU
        9         0         0                 7               6
end got connection

start execute
1 select * from (SELECT name,  value FROM v$sesstat,  v$statna        DICTIONARY LOOKUP CURSOR CACHED   ASANGA 34
2 select /*+ connect_by_filtering */ privilege#,level from sys        DICTIONARY LOOKUP CURSOR CACHED   SYS    34
3 insert into sys.aud$( sessionid,entryid,statement,ntimestamp        OPEN-RECURSIVE                    ASANGA 34
4 select value$ from props$ where name = 'GLOBAL_DB_NAME'             DICTIONARY LOOKUP CURSOR CACHED   SYS    34
5 select SYS_CONTEXT('USERENV', 'SERVER_HOST'), SYS_CONTEXT('U        DICTIONARY LOOKUP CURSOR CACHED   SYS    34
6 select decode(failover_method, NULL, 0 , 'BASIC', 1, 'PRECON        DICTIONARY LOOKUP CURSOR CACHED   SYS    34
7 select privilege# from sysauth$ where (grantee#=:1 or grante        DICTIONARY LOOKUP CURSOR CACHED   SYS    34
8 select b from x where a = :1                                        SESSION CURSOR CACHED             ASANGA 34
9 select rownum,sql_text,cursor_type,USER_NAME,sid from v$open        DICTIONARY LOOKUP CURSOR CACHED   ASANGA 34

Parse_Total Hard_Parse Ses_Cur_Cache_Hit Ses_Cur_Cache_Count CPU
        10         0         10001             9             151
end execute
The statistics values at connection checkout are identical to that of case 1. The test case output shows the total parse count increased by 1. This is same as case 1, even though in case 2 preparedstatement was created and closed with each iteration this has not resulted in additional parses. The reason for this is that a closed cursor was found in the session cache and resulted in a session cache cursor hit. This value is 10001 in the second test case. From the test case 1 it's known that SQL2 and SQL3 results in 2 session cache hits. This means SQL1 resulted in 9999 session cache hits when executing 10000 times. The one cache hit miss, the initial execution resulted in a single soft parse. Thus in terms number of parses done case 1 and case 2 are the same. However a tkprof output would list 10000 parses which is not necessarily the case. The total session cursor count is 9 same as test case 1 but the CPU time is high compared to previous test case and is at 145 (151-6) centi-seconds. This is the overhead of session cache cursor look up.

What would be the parse overhead for two cases when the session cached cursor parameter is set to 0. This would mean no cursors would be found in the session cache. Below is the output for case 1 when run with session cache cursor set to 0.
start got connection
1 insert into sys.aud$( sessionid,entryid,statement,ntimestamp        OPEN-RECURSIVE  ASANGA  157
2 select rownum,sql_text,cursor_type,USER_NAME,sid from v$open        OPEN            ASANGA  157

Parse_Total Hard_Parse Ses_Cur_Cache_Hit Ses_Cur_Cache_Count CPU
        9         0         0                 0               3
end got connection

start execute
1 insert into sys.aud$( sessionid,entryid,statement,ntimestamp        OPEN-RECURSIVE  ASANGA  157
2 select rownum,sql_text,cursor_type,USER_NAME,sid from v$open        OPEN            ASANGA  157

Parse_Total Hard_Parse Ses_Cur_Cache_Hit Ses_Cur_Cache_Count CPU
        12         0         0                 0              74
end execute
Number of parses at the connection checkout time is same as the test executed with SCC (session_cached_curosr) = 50. However there are no session cached cursors only opened ones. At the end of the execution total number of parses increased by 3. This is due to execution of SQL1, SQL2 and SQL3. Even though SQL1 was executed multiple times with same cursor (or preparedstatement) it only resulted in one soft parse which is same as test case 1 with SCC=50. Since there's no session cache SQL2 and SQL3 executed second time around also results in soft parses. The total CPU usage is 71 (74-3) centi-seconds which less compared to test 1 with SCC=50.
Output for test case 2 with SCC=0 is shown below.
start got connection
1 insert into sys.aud$( sessionid,entryid,statement,ntimestamp        OPEN-RECURSIVE   ASANGA  157
2 select rownum,sql_text,cursor_type,USER_NAME,sid from v$open        OPEN             ASANGA  157

Parse_Total Hard_Parse Ses_Cur_Cache_Hit Ses_Cur_Cache_Count CPU
        9         0         0                 0               3
end got connection

start execute
1 insert into sys.aud$( sessionid,entryid,statement,ntimestamp        OPEN-RECURSIVE   ASANGA  157
2 select rownum,sql_text,cursor_type,USER_NAME,sid from v$open        OPEN             ASANGA  157

Parse_Total Hard_Parse Ses_Cur_Cache_Hit Ses_Cur_Cache_Count CPU
   10011         0           0                0              198
end execute
Compared to previous case 1 there's a huge increase in the amount of soft parses. Total parse count was increased by 10002 (10011-9). This is as a result of 10000 parses for SQL1 and 1 parse each for SQL2 and SQL3. As a result the CPU usage has also increased which is 195 (198-3) centi-seconds for case 2. So out of all scenarios case 2 with SCC=0 would be the worse in terms of parse overhead and CPU.



It is possible to enable statement caching on the JDBC connection pool/non-pooled physical connection as opposed to database session cache. This is useful to lower the CPU and parse overhead when SCC=0 and there's no way to set SCC initialization parameter. This could be tested in the java code by uncommenting the setMaxStatements line. Below is the output for test case 2 with setMaxStatements value set to 10.
start got connection
1 insert into sys.aud$( sessionid,entryid,statement,ntimestamp        OPEN-RECURSIVE   ASANGA   157
2 select rownum,sql_text,cursor_type,USER_NAME,sid from v$open        OPEN             ASANGA   157

Parse_Total Hard_Parse Ses_Cur_Cache_Hit Ses_Cur_Cache_Count CPU
        9         0         0                 0               3
end got connection

start execute
1 select * from (SELECT name,  value FROM v$sesstat,  v$statna        OPEN             ASANGA  157
2 insert into sys.aud$( sessionid,entryid,statement,ntimestamp        OPEN-RECURSIVE   ASANGA  157
3 select b from x where a = :1                                        OPEN             ASANGA  157
4 select rownum,sql_text,cursor_type,USER_NAME,sid from v$open        OPEN             ASANGA  157

Parse_Total Hard_Parse Ses_Cur_Cache_Hit Ses_Cur_Cache_Count CPU
        10         0         0                 0             107
end execute
In this case the total parse count was only increased by 1 and CPU usage is low compared to test case 2 with SCC=0. This comparable to test cases with SCC=50 in terms CPU usage and parse count.
The parse counts and CPU usage for each test case could be summarized as below. SCC is session cached cursor value and MaxStm is the value set on setMaxStatements method.
TestParse countCPU (centi-seconds)
SCC=50,Case=1,MaxStm=0188
SCC=50,Case=2,MaxStm=01145
SCC=0,Case=1,MaxStm=0371
SCC=0,Case=2,MaxStm=010002195
SCC=0,Case=1,MaxStm=10197
SCC=0,Case=2,MaxStm=101104

CPU usage graph shown below.

Tests has shown that setting or not setting session cached cursor parameter has a higher degree of influence in terms of parse count when comparing case 1 and case 2. When session cached cursor is not set statement caching could be achieved with the help of connection pools and in terms of parse count this is comparable to scenario with session cached cursor set.
Table used for test
create table x (a number, b varchar2(10));

begin
  for i in 1 .. 10
  loop
  insert into x values (i, 'abc'||i);
  end loop;
end;
/
Java code used for test
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;

/**
 *
 * @author Asanga
 */
public class CursorTest {

    static String SQL1 = "select b from x where a = ?";
    static String SQL2 = "select * from (SELECT name,  value FROM v$sesstat,  v$statname WHERE v$sesstat.statistic#=v$statname.statistic# "
            + "AND (v$statname.name in ('parse count (total)','parse count (hard)','session cursor cache hits','session cursor cache count','CPU used by this session'))"
            + " AND v$sesstat.sid= sys_context('USERENV','SID')"
            + " )pivot (sum(value) for name in ('parse count (total)' as Parse_Total,'parse count (hard)' Hard_Parse,'session cursor cache hits' as SES_CUR_CACHE_HITS,'session cursor cache count' as SES_CUR_CACHE_COUNT, 'CPU used by this session' as CPU))";
    static String SQL3 = "select rownum,sql_text,cursor_type,USER_NAME,sid from v$open_cursor where sid=sys_context('USERENV','SID')";

    public static void main(String[] args) throws Exception {
        try {

            PoolDataSource ds = PoolDataSourceFactory.getPoolDataSource();
            ds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
            ds.setConnectionPoolName("TTPool");
            ds.setURL("jdbc:oracle:thin:@192.168.0.93:1521/dbsrv1");
            ds.setUser("asanga");
            ds.setPassword("asa");
//            ds.setMaxStatements(10);

            Connection con = ds.getConnection();
            PreparedStatement pr = null;

            System.out.println("start got connection");
            stat(con);
            System.out.println("end got connection");

            System.out.println("start execute");
            ResultSet rs = null;

            /****************************** Start CASE 1 ************************************/
            pr = con.prepareStatement(SQL1);
            for (int i = 0; i < 10000; i++) {
                pr.setInt(1, i % 10);
                rs = pr.executeQuery();

                while (rs.next()) {
                    rs.getString(1);
                }
                rs.close();
            }

            pr.close();
            /****************************** End CASE 1 ************************************/
            /****************************** Start CASE 2 ************************************/
//            for (int i = 0; i < 10000; i++) {
//                pr = con.prepareStatement(SQL1);
//                pr.setInt(1, i%10);
//                rs = pr.executeQuery();
//
//                while (rs.next()) {
//                    rs.getString(1);
//                }
//                rs.close();
//                pr.close();
//            }
            /****************************** End CASE 2 ************************************/
            stat(con);
            System.out.println("end execute");
            con.close();

        } catch (SQLException ex) {
            Logger.getLogger(CursorTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void stat(Connection con) throws SQLException {

        PreparedStatement pr = null;
        ResultSet rs = null;

        pr = con.prepareStatement(SQL3);
        rs = pr.executeQuery();
        while (rs.next()) {
            System.out.println(rs.getString(1) + " " + rs.getString(2) + "\t" + rs.getString(3) + " " + rs.getString(4) + " " + rs.getString(5));
        }
        rs.close();
        pr.close();

        System.out.println("Parse_Total Hard_Parse Ses_Cur_Cache_Hit Ses_Cur_Cache_Count CPU");
        pr = con.prepareStatement(SQL2);
        rs = pr.executeQuery();

        while (rs.next()) {

            System.out.println("\t" + rs.getString(1) + "\t " + rs.getInt(2) + "\t " + rs.getString(3) + "\t\t " + rs.getInt(4) + "\t       " + rs.getInt(5));
        }

        rs.close();
        pr.close();

    }
}

Related Post
Session Cached Cursors and JDBC CallableStatement

Tuesday, May 14, 2013

Unsupported Bind Variable Syntax in PreparedStatments and 10.2/11.1/11.2 JDBC Drivers

Oracle supports two kind of bind variable placeholder syntax in Java PreparedStatments. A bind variable placeholder could be denoted with a colon and a number ":1" or with a question mark "?". However there are situation where developers may use a syntax such as question mark followed by a number "?1". Though this syntax is not supported in Oracle, when using JDBC drivers 10.2 and 11.1 the Java code would execute without an error and returns results as expected. But with 11.2 JDBC driver this will give the error ORA-00933: SQL command not properly ended and java code would not execute as before. So as part of database upgrade if JDBC driver is also upgraded and if java code has the syntax similar to "?1" etc then code that previously worked would not work anymore.
The problem here lies in using the unsupported bind variable placeholders. According to Oracle the fact that it works with previous version of JDBC driver is a merer accident and not the expected behavior. If it's not possible to modify the code the easiest option to remedy to the situation is to downgrade the JDBC driver to 11.1. As the issue comes as a result of the JDBC driver not because of the database
Java code given at the end of the post could be used to test the unsupported syntax against various databases and JDBC drivers. Following table list summary of findings from running the test code with 10.2/11.1/11.2 JDBC drivers and databases.

JDBC Driver VersionDatabase Version"?1" Works
10.2.0.510.2.0.5YES
10.2.0.511.1.0.7YES
10.2.0.511.2.0.3YES
11.1.0.710.2.0.5YES
11.1.0.711.1.0.7YES
11.1.0.711.2.0.3YES
11.2.0.310.2.0.5NO
11.2.0.311.1.0.7NO
11.2.0.311.2.0.3NO




Create and populate table used in the test code
SQL> create table x (a varchar2(10), b number);

Table created.

SQL> begin
  2  for i in 1 .. 20
  3  loop
  4  insert into x values('abc'||i,i);
  5  end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.
Test code
   
        OracleDataSource ds = new OracleDataSource();
        ds.setUser("asanga");
        ds.setPassword("asa");
        ds.setURL("jdbc:oracle:thin:@192.168.0.66:1521:ent11g2");

        Connection con = ds.getConnection();
        DatabaseMetaData meta = con.getMetaData();

        System.out.println("Driver Name " + meta.getDriverName());
        System.out.println("Driver Version " + meta.getDriverVersion());
        System.out.println("Driver Major Version " + meta.getDriverMajorVersion());
        System.out.println("Driver Minor Version " + meta.getDriverMinorVersion());
        System.out.println("Database Major Version " + meta.getDatabaseMajorVersion());
        System.out.println("Database Minor Version " + meta.getDatabaseMinorVersion());
        System.out.println("Database Product Name " + meta.getDatabaseProductName());
        System.out.println("Database Product Version " + meta.getDatabaseProductVersion());

        String SQL = "select * from x where b = ?1"; // works on 10gR2, 11gR1 jdbc driver but not on 11gR2 jdbc driver
//        String SQL = "select * from x where b = ?";  // works on all drivers

        PreparedStatement pr = con.prepareStatement(SQL);
        pr.setInt(1, 10);
        ResultSet rs = pr.executeQuery();

        while(rs.next()){

            System.out.println(rs.getString("A")+" "+rs.getInt("B"));
        }
              
        rs.close();
        pr.close();
        con.close();

Useful metalink notes
ORA-00933 When Using Bind Variables in JDBC 11.2 [ID 1304235.1]

Tuesday, August 7, 2012

Variable Length In-Clause : SQL vs PL/SQL Method Comparison

When the number of variables in a in-clause varies this results in a new statement being created. The widely available solution (after some googling) for this is to create a PL/SQL function and use a table type to run the in-clause where only one statement is created and will get re-used irrespective of number of parameters. This is a comparison between these two options of using the PL/SQL method and creating unique SQL for each set of in-clause variables.
For PL/SQL option create a table type and function as below
create or replace type mytabletype as table of number;

create or replace function mytablefun(input in varchar2) return
mytabletype
as 
    l_str   LONG DEFAULT input || ',';
    l_n     NUMBER;
    l_data  mytabletype := mytabletype();
BEGIN
    LOOP
        l_n := instr( l_str, ',' );
        exit when (nvl(l_n,0) = 0);
        l_data.extend;
        l_data( l_data.count ) := ltrim(rtrim(substr(l_str,1,l_n-1)));
        l_str := substr( l_str, l_n+1 );
    END LOOP;
    RETURN l_data;
END;
/
The test case is run using a java program which generate the sql statments. Code is given below
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class InclauseTest {

    public static void main(String[] args)  {
        try {

            //Connection pool creation code eg. OracleDataSource or UDP

            Connection con = pool.getConnection();

            long t1 = System.currentTimeMillis();

            newsqlMehtod(con);       //runs sql method test
//            newfunctionMethod(con);  // uncomment to run pl/sql method test

            long t2 = System.currentTimeMillis();

            DBStats.displayStats(con);


            System.out.println("\n\n total time taken : "+(t2-t1));
            con.close();

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }


 public static void newsqlMehtod(Connection con){

        String sql1 = "select * from x where a in (";
                        
        for (int i = 0 ; i < 10000; i++){
            
            try {
                int k = i % 10;
                PreparedStatement pr = con.prepareStatement(sql1+prepareStringCreate(k)+")");

                preparePopulate(pr, k+1);


                ResultSet rs = pr.executeQuery();

                while(rs.next()){

                    int q = rs.getInt(1);
                    double p = rs.getDouble(2);
                }

                rs.close();
                pr.close();


            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

  public static String prepareStringCreate( int i){
        
        StringBuilder x = new StringBuilder("?");

        for(int j = 0; j< i; j++){
            
            x.append(",?");
        }
        
        return x.toString();
        
    }

  public static void preparePopulate(PreparedStatement pr, int i) throws SQLException{

        for(int j = 0; j< i; j++){

            pr.setInt(j+1, (j+1)* 10);
        }

    }

 public static void newfunctionMethod(Connection con){

             String sql = "select * from x where a in (select * from table(SELECT CAST( mytablefun( ? ) AS mytabletype ) FROM dual))";

        for (int i = 0 ; i < 10000; i++){
            try {
                PreparedStatement pr = con.prepareStatement(sql);

               StringBuilder x = new StringBuilder("10");

               int q = i%10;

               for (int j = 0; j<q  ; j++){

                   x.append(","+((j+2)*10));
               }

                
                pr.setString(1, x.toString());
                ResultSet rs = pr.executeQuery();

                while(rs.next()){

                    int k = rs.getInt(1);
                    double p = rs.getDouble(2);
                }

                rs.close();
                pr.close();

            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

    }
}

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

public class DBStats {

    public static void displayStats(Connection con) {
        try {
            PreparedStatement pr = con.prepareStatement("select name,value " + "from v$mystat,v$statname " + "where v$mystat.statistic#=v$statname.statistic# " 
//                    + "and v$statname.statistic# in (11,12)"); //11gR1
                    + "and v$statname.statistic# in (16,17)"); //11gR2

            ResultSet rs = pr.executeQuery();


            while(rs.next()){

                System.out.println(rs.getString(1)+" "+rs.getDouble(2));
            }

            rs.close();
            pr.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }

    }
}
Running the test case once will result in 10000 sql statements being executed with number of values used in the in-clause varying from 1 - 10. The DBStats class is used to get CPU used by the session values. The java test program is run twice (resulting in 20000 executions) and SQL statistics are taken. Shared pool is flushed before the run but not between each run (so the statistics are cumulative).


First the results from SQL method. The session stats gives
first run
CPU used when call started 47.0
CPU used by this session 49.0

second run
CPU used when call started 43.0
CPU used by this session 43.0
SQL stats
SQL> select sql_id,cpu_time,elapsed_time,executions,SHARABLE_MEM,PERSISTENT_MEM,RUNTIME_MEM,
sql_text from v$sql where sql_text like 'select * from x where a in%' order by sql_text;

SQL_ID          CPU_TIME ELAPSED_TIME EXECUTIONS SHARABLE_MEM PERSISTENT_MEM RUNTIME_MEM SQL_TEXT
------------- ---------- ------------ ---------- ------------ -------------- ----------- ----------------------------------------------------------------------
26fb47dhnuz88      28994        94332       2000        19753           4496        3408 select * from x where a in (:1 )
068anap1mvbdx      46993       105790       2000        15701           4648        3528 select * from x where a in (:1 ,:2 )
387t0d508fkzb      55993       107300       2000        19777           4728        3576 select * from x where a in (:1 ,:2 ,:3 )
3hm2vcm9q6zup      35994       108100       2000        19797           4808        3624 select * from x where a in (:1 ,:2 ,:3 ,:4 )
7fg42cswna64p      55992       107610       2000        19825           4888        3672 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 )
89k5ysfx65zpu      52992       109258       2000        19845           4968        3720 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 ,:6 )
32w3mnqx0vhf4      40994       109297       2000        19865           5048        3768 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 ,:6 ,:7 )
4yxvar786t71u      63987       110639       2000        19885           5128        3816 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 ,:6 ,:7 ,:8 )
7x0jrn3nj8ux6      54987       112536       2000        19913           5208        3864 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 ,:6 ,:7 ,:8 ,:9 )
0z3udxfqg2p0p      57998       112400       2000        19942           5288        3912 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 ,:6 ,:7 ,:8 ,:9 ,:10 )

10 rows selected.
As expected the SQL method results in 10 statements being created and each getting equal number of executions. Stats for each executions, although elapsed time per exec is roughly same CPU time values vary.
SQL> SELECT sql_id,
  2    cpu_time,
  3    round(cpu_time/executions,2) as cpu_per_exec,
  4    elapsed_time,
  5    round(elapsed_time/executions,2) as ela_per_exec,
  6    executions,
  7  --  SHARABLE_MEM,
  8  --    PERSISTENT_MEM,
  9  --  RUNTIME_MEM,
 10    sql_text
 11  FROM v$sql
 12  WHERE sql_text LIKE 'select * from x where a in%'
 13  ORDER BY sql_text;

SQL_ID        CPU_TIME CPU_PER_EXEC ELAPSED_TIME ELA_PER_EXEC EXECUTIONS SQL_TEXT
------------- -------- ------------ ------------ ------------ ---------- ---------------------------------------------------------------------
26fb47dhnuz88    28994         14.5        94332        47.17       2000 select * from x where a in (:1 )
068anap1mvbdx    46993         23.5       105790         52.9       2000 select * from x where a in (:1 ,:2 )
387t0d508fkzb    55993           28       107300        53.65       2000 select * from x where a in (:1 ,:2 ,:3 )
3hm2vcm9q6zup    35994           18       108100        54.05       2000 select * from x where a in (:1 ,:2 ,:3 ,:4 )
7fg42cswna64p    55992           28       107610        53.81       2000 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 )
89k5ysfx65zpu    52992         26.5       109258        54.63       2000 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 ,:6 )
32w3mnqx0vhf4    40994         20.5       109297        54.65       2000 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 ,:6 ,:7 )
4yxvar786t71u    63987        31.99       110639        55.32       2000 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 ,:6 ,:7 ,:8 )
7x0jrn3nj8ux6    54987        27.49       112536        56.27       2000 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 ,:6 ,:7 ,:8 ,:9 )
0z3udxfqg2p0p    57998           29       112400         56.2       2000 select * from x where a in (:1 ,:2 ,:3 ,:4 ,:5 ,:6 ,:7 ,:8 ,:9 ,:10 )
Next is the PL/SQL method, again java program is run twice and shared pool is flushed before the run but not between the runs. The session stats gives
first run
CPU used when call started 479.0
CPU used by this session 481.0

second run
CPU used when call started 267.0
CPU used by this session 267.0
CPU used by session is high compared to the SQL statement method earlier. The sql stats
SQL_ID          CPU_TIME ELAPSED_TIME EXECUTIONS SHARABLE_MEM PERSISTENT_MEM RUNTIME_MEM SQL_TEXT
------------- ---------- ------------ ---------- ------------ -------------- ----------- ----------------------------------------------------------------------
1p40d61qp4u6g    6333007      7284060      19997        28748           7040        5328 select * from x where a in (select * from table(SELECT CAST( mytablefu
                                                                                         n( :1  ) AS mytabletype ) FROM dual))

1p40d61qp4u6g      91986        91943          3        28748           7040        5328 select * from x where a in (select * from table(SELECT CAST( mytablefu
                                                                                         n( :1  ) AS mytabletype ) FROM dual))
Per exec values
SQL_ID            CPU_TIME CPU_PER_EXEC ELAPSED_TIME ELA_PER_EXEC EXECUTIONS SQL_TEXT
------------- ------------ ------------ ------------ ------------ ---------- ---------------------------------------------------------------------
1p40d61qp4u6g      6333007        316.7      7284060       364.26      19997 select * from x where a in (select * from table(SELECT CAST( mytablef
                                                                             un( :1  ) AS mytabletype ) FROM dual))

1p40d61qp4u6g        91986        30662        91943     30647.67          3 select * from x where a in (select * from table(SELECT CAST( mytablef
                                                                             un( :1  ) AS mytabletype ) FROM dual))
First some clarification as to why there are two sets of statments when only one is expected. Looking at the sql shared view
SQL> SELECT sql_id,
  2  child_number,
  3  address,
  4  executions,sql_text
  5  FROM v$sql
  6  WHERE sql_text LIKE 'select * from x where a in%'
  7  ORDER BY sql_text;

SQL_ID        CHILD_NUMBER ADDRESS          EXECUTIONS SQL_TEXT
------------- ------------ ---------------- ---------- ----------------------------------------------------------------------
1p40d61qp4u6g            1 00000002088B14D0      19997 select * from x where a in (select * from table(SELECT CAST( mytablefu
                                                       n( :1  ) AS mytabletype ) FROM dual))

1p40d61qp4u6g            0 00000002088B14D0          3 select * from x where a in (select * from table(SELECT CAST( mytablefu
                                                       n( :1  ) AS mytabletype ) FROM dual))
                
                
SQL> select sql_id,address,child_number,use_feedback_stats from V$SQL_SHARED_CURSOR where sql_id='1p40d61qp4u6g'  and address='00000002088B14D0';

SQL_ID        ADDRESS          CHILD_NUMBER USE_FEEDBACK_STATS
------------- ---------------- ------------ --------------------
1p40d61qp4u6g 00000002088B14D0            0 Y
1p40d61qp4u6g 00000002088B14D0            1 N
"Y" on use_feedback_stats means "A hard parse is forced so that the optimizer can reoptimize the query with improved cardinality estimates" (From Oracle docs).

The comparison is made on cpu time and shareable memory. First the cpu time comparison
Sum of cpu time from the SQL method test = 494924
Sum of cpu time from the PL/SQL method test = 6424993
It could be seen that PL/SQL method uses over 1198% more CPU time than the SQL method.

Comparing the shareable memory used by two methods
Sum of shareable memory from SQL method test = 194303
Sum of shareable memory from PL/SQL method test = 57496
It could be seen due to creation of multiple statements SQL method uses 238% more shareable memory than the PL/SQL method.

The comparison shows the two methods have trade-off between cpu and memory.