5.7 Bound Parameters

     

One of the easiest ways to optimize a slow database application that executes many similar SQL statements is to parameterize the most commonly used SQL statements. Parameterization is a way of reusing a SQL statement by writing it with placeholders for frequently changing values. This provides two major benefits: the potential for reduced server roundtrips and better processing efficiency on the database server, since the server doesn't need to parse, plan, and optimize the execution of frequently used SQL statements on every execution.

Parametrized statements are useful in using string or binary values within SQL statements that contain unfriendly characters, such as quote marks or terminal NULL characters that indicate the end of a string. Usage in this way provides security benefits to a database application where user input is used within statements. Not binding a parameter coming from an end user could allow clever modification of the statement in a way that may divulge secret information from the database.

Since there is a slight cost involved in setting up a parameterized statement, it's good practice to only parameterize statements that will be executed at least three times prior to the statement closing. If a statement is only executed once prior to freeing the statement, the costs of preparing the statement and binding the parameters could result in a performance net loss to the application.


5.7.1 ADO.NET Bound Parameters

The following C# code fragment executes a SQL INSERT statement that adds new sales to the sales table in the pubs database. The INSERT statement is parameterized to provide better performance, since the statement object needs to be parsed only once on the server.

 // Create a Command object for the SQL statement Statement   statement   =   connection   .CreateCommand( );   statement   .CommandText =       "INSERT INTO SALES(stor_id,                         ord_num,                         ord_date,                         qty,                         payterms,                         title_id) " +     "VALUES(@stor_id, @ord_num, @ord_date, @qty, @payterms, @title_id)"; //Prepare the statement on the server   statement   .Prepare( ); // Declare parameters that will be bound {OdbcOleDbSql}Parameter stor_id, ord_num, ord_date,                            qty, payterms, title_id; stor_id =   statement   .Parameters.Add( "@stor_id", DbType.String ); ord_num =   statement   .Parameters.Add( "@ord_num", DbType.String ); ord_date =   statement   .Parameters.Add( "@ord_date", DbType.DateTime); qty =   statement   .Parameters.Add( "@qty", DbType.Int16 ); payterms =   statement   .Parameters.Add( "@payterms", DbType.String ); title_id =   statement   .Parameters.Add( "@title_id", DbType.String ); while( GetNextSale(stor_id, ord_num, ord_date, qty, payterms, title_id) ) {     // Execute the statement     int result =   statement   .ExecuteNonQuery( );     if( result != 1 )     {         // If result isn't 1, then the insert failed.         System.Console.WriteLine( "The INSERT failed." );         break;     } } 

5.7.1.1 Use the following steps to execute statements with bound parameters in ADONET:
  1. As done in previous sections, we create an ADO.NET Command object and assign a SQL statement to it. The difference for using bound parameters within the statement is in the VALUES clause of the INSERT statement. Contained within the VALUES clause are six named placeholders for the parameters that will be later bound to the Command object. In ADO.NET the placeholders begin with the @ symbol and are followed by an identifier that is unique amongst all placeholders in the statement.

     Statement   statement   =   connection   .CreateCommand( );   statement   .CommandText =       "INSERT INTO SALES(stor_id,                         ord_num,                         ord_date,                         qty,                         payterms,                         title_id) " +     "VALUES(@stor_id, @ord_num, @ord_date, @qty, @payterms, @title_id)"; 

  2. Invoke the Prepare method on the Command object to prepare the SQL statement for execution. This notifies the database layer that the Command object will be executed with bound parameters.

       statement   .Prepare( ); 

  3. Declare the parameter objects using the parameter type that matches the Command object: OdbcParameter , OleDbParameter , or SqlParameter . After declaring the parameters, create parameter objects by invoking the Add method of the Command object's Parameter collection and assign the return value to the Parameter object. The first argument to the Add method is the name of the placeholder that the parameter will map to during execution. In the example below, the stor_id Parameter object will map to the first item in the VALUES clause, which has the placeholder named @stor_id . The second argument in the Add method is the column type that the placeholder maps to on the server. Look to Table 5-8 for a list of frequently used types.

     {OdbcOleDbSql}Parameter stor_id, ord_num, ord_date,                            qty, payterms, title_id; stor_id =   statement   .Parameters.Add( "@stor_id", DbType.String ); ord_num =   statement   .Parameters.Add( "@ord_num", DbType.String ); ord_date =   statement   .Parameters.Add( "@ord_date", DbType.DateTime); qty =   statement   .Parameters.Add( "@qty", DbType.Int16 ); payterms =   statement   .Parameters.Add( "@payterms", DbType.String ); title_id =   statement   .Parameters.Add( "@title_id", DbType.String ); 

  4. In this example, the Parameter objects are assigned a value by the user-defined GetNextSale function call, which could be implemented like this:

     static bool GetNextSale(SqlParameter stor_id,                          SqlParameter ord_num,                          SqlParameter ord_date,                          SqlParameter qty,                         SqlParameter payterms,                          SqlParameter title_id) {     // Omitted is the code that would     // Read a sale record from a file, or user input, etc.     // If there are no more sale records, return false.     if( !more_records ) return false;     // Assign values to the parameter objects     stor_id.Value  = 1234;     ord_num.Value  = "ABCD.123";     ord_date.Value = new DateTime(2003,2,24);     qty.Value      = 50;     payterms.Value = "Net 60";     title_id.Value = "SD2043";     return true; } 

    Notice that the parameters are assigned a value by assigning directly to the Value property on the Parameter object. The stor_id parameter object is assigned a value of 1234, whereas ord_date is assigned a C# ADO.NET DateTime object. The function returns false if there are no more sales to insert into the table; otherwise the function returns true.

    Combined with a while loop, the program will continue to insert new sales into the database until the GetNextSale function runs out of new records to process.

     while(GetNextSale(stor_id, ord_num, ord_date, qty, payterms, title_id) ) { 

  5. Invoking the ExecuteNonQuery method on the Command object executes the INSERT statement, with the bound parameter values replaced for their corresponding placeholders. The ExecuteNonQuery method returns the number of rows affected, which will be 1 in this case on a successful single-row insert. This return value is used in error handling and the application will exit if the statement should ever fail to execute.

     // Execute the statement     int result =   statement   .ExecuteNonQuery( );     if( result != 1 )     {         // If result isn't 1, then the insert failed.         System.Console.WriteLine( "The INSERT failed." );         break;     } 

Table 5-8. Frequently used parameter object types

DbType object type

Description

AnsiString

Variable-length, non-Unicode character string between 1 and 8,000 characters.

AnsiStringFixedLength

Fixed-length, non-Unicode character string.

Binary

Variable-length binary string between 1 and 8,000 bytes.

Boolean

Represents a true or false value.

Byte

Unsigned integer value ranging 0 to 255.

Currency

Currency value ranging from -2 63 to 2 63 -1.

DateTime

Date and time value.

Decimal

Numeric value ranging from 10 -28 to 7.9 10 28 with about 28 significant digits.

Double

Floating point type ranging from 5.0 10 -324 to 1.7 10 308 with about 15 digits.

Int{16,32,64}

Signed integer; the number suffix indicates the number of bits of precision. For example, Int32 is a 32-bit integer.

Single

Floating point type ranging from 5.0 10 -324 to 1.7 10 308 , with about 15 digits.

String

Variable-length, Unicode character string.

StringFixedLength

Fixed-length, Unicode character string.

UInt{16,32,64}

Unsigned integer; the number suffix indicates the number of bits of precision. For example, UInt32 is a 32-bit unsigned integer.


5.7.2 Binding Parameters with JDBC

The following Java code fragment executes a SQL INSERT statement that adds new sales to the sales table in the pubs database. The INSERT statement is parameterized to provide better performance.

 // Create a Command object for the SQL statement PreparedStatement   statement   =   connection   .prepareStatement(     "INSERT INTO SALES(stor_id,                         ord_num,                         ord_date,                         qty,                         payterms,                         title_id) " +     "VALUES(?, ?, ?, ?, ?, ?)" ); while( getNextSale(   statement   ) ) {     // Execute the statement     int result =   statement   .executeUpdate( );     if( result != 1 )     {         // If result isn't 1, then the insert failed.         System.out.println( "The INSERT failed." );         break;     } } 

5.7.2.1 Use the following steps to execute statements with bound parameters in JDBC:
  1. Create a JDBC PreparedStatement object and pass the parameterized SQL statement into its constructor. The difference for using bound parameters within the statement is in the VALUES clause of the INSERT statement. Contained within the VALUES clause are six placeholders (the question marks) for the parameters that will later be bound to the PreparedStatement object.

 PreparedStatement   statement   =   connection   .prepareStatement(     "INSERT INTO SALES(stor_id,                         ord_num,                         ord_date,                         qty,                         payterms,                         title_id) " +     "VALUES(?, ?, ?, ?, ?, ?)" ); 

  1. In this example, the parameters are assigned a value by the user-defined getNextSale function call, which could be implemented like this:

 static boolean getNextSale( PreparedStatement   statement   )      throws SQLException {     // Omitted is the code that would     // Read a sale record from a file, or user input, etc.     // If there are no more sale records, return false.     if( !more_records ) return false;   statement   .setString(1, "1234");   statement   .setString(2, "ABCD.123");   statement   .setDate(3, new java.sql.Date(2003, 2, 24));   statement   .setInt(4,50);   statement   .setString(5, "Net 60");   statement   .setString(6, "SD2043");     return true; } 

  1. Each binding position is referenced by its ordinal position in the SQL statement, with the first position starting at 1. The values are assigned to placeholders using the set methods found on the PreparedStatement object. Table 5-9 contains a list of frequently used set methods.

  2. The function returns false if there are no more sales to insert into the table; otherwise the function returns true.

  3. Combined with a while loop, the program will continue to insert new sales into the database until the getNextSale function runs out of new records to process.

 while( getNextSale(   statement   ) ) { 

  1. Invoking the executeUpdate method on the PreparedStatement object executes the INSERT statement, with the bound parameter values replaced for their corresponding placeholders. The executeUpdate method returns the number of rows affected, which will be 1 in this case on a successful single-row insert. This return value is used in error handling and the application will exit if the statement should ever fail to execute.

 // Execute the statement     int result =   statement   .executeUpdate( );     if( result != 1 )     {         // If result isn't 1, then the insert failed.         System.out.println( "The INSERT failed." );         break;     } } 

Table 5-9. Frequently used PreparedStatement set methods

Method name

Description

setBlob(int i , Blob value )

Sets the placeholder at position i to the Blob contained in value .

setBoolean(int i , boolean value )

Sets the placeholder at position i to the boolean contained in value .

setByte(int i , byte value )

Sets the placeholder at position i to the byte contained in value .

setClob(int i , Clob value )

Sets the placeholder at position i to the Clob contained in value .

setDate(int i , Date value [, Calendar cal ])

Sets the placeholder at position i to the Date contained in value . If cal is provided, it'll be used to interpret the Date value .

setDouble(int i , double value )

Sets the placeholder at position i to the double contained in value .

setFloat(int i , float value )

Sets the placeholder at position i to the float contained in value .

setInt(int i , int value )

Sets the placeholder at position i to the int contained in value .

setLong(int i , long value )

Sets the placeholder at position i to the long contained in value .

setNull(int i , int v )

Sets the placeholder at position i to a NULL value when v is true.

setString(int i , String value )

Sets the placeholder at position i to the String contained in value .

setTimestamp(int i , Timestamp value [,Calendar cal ])

Sets the placeholder at position i to the Timestamp contained in value . If cal is provided, it'll be used to interpret the Timestamp value .




SQL in a Nutshell
SQL in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596518846
EAN: 2147483647
Year: 2003
Pages: 78

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net