146 Integrating DB2 Universal Database for iSeries with Microsoft ADO .NET
In this section, we discuss some of the things you can do to make the best use of the IBM
DB2 UDB for iSeries provider.
Read about iSeries database performance topics in the IBM Information Center at:
http://www.iseries.ibm.com/infocenter
Select Database → Performance and optimization.
Chapter 4. IBM DB2 UDB for iSeries .NET provider 147
Read the “Indexing and statistics strategies for DB2 UDB for iSeries” white paper at:
http://www.ibm.com/servers/enable/site/education/ibo/record.html?indxng
Attend the DB2 Performance Workshop; information at:
http://www.ibm.com/servers/eserver/iseries/service/igs/db2performance.html
Study the DB2 UDB for iSeries online courses found at:
http://www.ibm.com/servers/enable/site/education/ibo/view.html?oc#db2
Use stored procedures to group many operations into a single call. This can reduce the
number of times data flows across the communication link. You can also use stored
procedures to wrap programs or CL commands to catalog the parameter definitions so
DeriveParameters() can be used on the call. For information about how to write stored
procedures in the iSeries, go to the IBM Information Center at:
http://www.iseries.ibm.com/infocenter
Select Database → Programming → SQL Programming → Routines → Stored
Procedures.
Another good reference is the IBM Redbook Stored Procedures, Triggers and User
Defined Functions on DB2 Universal Database for iSeries, SG24-6503, at:
http://www.redbooks.ibm.com/abstracts/sg246503.html
When using stored procedures, follow the guidelines spelled out in 4.5.5, “Calling stored
procedures” on page 79. Using stored procedures with CommandType.Text and including
your own parameter markers speeds up the time it takes to prepare the statement for
execution.
If you plan to execute a particular statement more than once, you can do several things to
improve the time it takes to prepare and execute the statement:
a. Use parameter markers in your statement instead of literal values. Then, each time you
execute the statement, your CommandText stays the same and only the parameter
values change. This approach enables the iSeries server to optimize the statement
once and subsequent calls to execute the statement take advantage of the optimization
already done. Parameter markers are discussed in 4.5.4, “Using parameters in your
SQL statements” on page 74. To take full advantage of this optimization, be sure your
statement does not contain any literal values.
b. Keep the CommandText the same for a statement you plan to execute many times.
While you can reuse the same iDB2Command object for running different SQL
statements, do this only when you do not plan to re-execute a statement. Each time
you change a command’s CommandText, the statement must be re-prepared before it
is executed (the provider does this for you). By keeping the CommandText constant,
you avoid this extra step.
Use tables whose columns are not nullable. When using the CommandBuilder, every
nullable column requires an extra check when performing updates and deletes. See 4.7.4,
“Using iDB2CommandBuilder” on page 139 for more information about using the
CommandBuilder. Also, when using a DataReader with columns that could contain null
data, an extra call to IsDBNull is required to check for null before calling any of the
DataReader’s Get methods.
If your application does not have to use all columns of a table, do not select all of the
columns. Fewer selected columns means less data sent over the communication link. The
exception to this is when you use the CommandBuilder, because with CommandBuilder,
your Select statement must include all of the primary or unique key columns.
If your application does not have to use all the rows of a table, do not select all of the rows.
Fewer rows selected means less data sent over the communication link.
148 Integrating DB2 Universal Database for iSeries with Microsoft ADO .NET
If you plan to make frequent calls to fill a DataAdapter, it may make more sense to open
and close the connection yourself, rather than letting the DataAdapter do it for you.
Although connection pooling helps improve connection startup performance, closing and
reopening a connection causes the provider to re-prepare any commands it may have
prepared on the previously open connection.
Connection pooling, which is enabled by default, can help improve your performance. To
take advantage of connection pooling, each connection you wish to pool must use exactly
the same ConnectionString. See 4.7.6, “Connection pooling” on page 143.
RLE compression, which is enabled via the DataCompression attribute in your
ConnectionString, is turned on by default. RLE compression normally helps application
performance, but some overhead is involved in compressing and decompressing data. By
testing your application with your normal application data, you may find that setting
DataCompression one way or the other can produce better performance.
When using large objects (LOBs) or large data sets, consider using a DataReader instead
of a DataAdapter. With a DataReader, you have more control over whether, when, and
how you read your data. For more about using LOBs with the IBM.Data.DB2.iSeries
provider, see 4.7.2, “Using large objects (LOBs)” on page 132.
When using LOBs with a DataReader or with your own insert, update, or delete
commands, you can optimize when the LOB data is read or written by using the
MaximumInlineLobSize property. See “MaximumInlineLobSize” on page 58.
Before executing your command, you do not have to call Prepare() first, as you may be
used to doing with other database technologies. The provider always does an implicit
prepare on your behalf when one of the Execute or DeriveParameters methods is called.
Because the provider always does an implicit prepare when needed, you do not see a
performance improvement by defining your own parameters. Instead, we encourage the
use of the iDB2Command object’s DeriveParameters() method.
The .NET common language runtime relies on garbage collection to handle the cleanup of
objects. This garbage collection is non-deterministic; it happens at unspecified times. To
make the best use of your application’s resources, you should call an object’s Close() or
Dispose() method (when one exists) when you are finished using the object. This ensures
that resources associated with your object are released when you know they are no longer
needed. This is especially important with objects that connect to the host
(iDB2Connection, iDB2Command, iDB2DataReader, and iDB2DataAdapter).
Choose the best Execute method for your SQL statement. See 4.5.6, “Choosing your
execute method” on page 86 for more information. Do not use ExecuteReader if
ExecuteNonQuery or ExecuteScalar will do.
With the IBM.Data.DB2.iSeries provider, normally you do not have to specify a Database
property in your ConnectionString (see “Database” on page 51). If you are not using an
independent auxiliary storage pool (IASP) with your application, then do not use the
Database property.
Use data types that are appropriate to your task. When using char data tagged with
CCSID 65535, see “iDB2CharBitData and iDB2VarCharBitData” on page 90.
When using a CommandBuilder, follow the guidelines discussed in 4.7.4, “Using
iDB2CommandBuilder” on page 139.
Under normal circumstances, do not enable tracing or diagnostics. Any time these are
enabled, extra processing time and computer resources are used to gather tracing and
diagnostic information. When you are finished with problem determination, disable your
traces and diagnostics.
Avoid using special characters in your SQL names (for instance, in table and column
names). To include mixed upper-case and lower-case names in tables and columns, and
to include special characters in the names, some names must be delimited using quote
characters. We call these types of identifiers delimited identifiers. Using delimited
identifiers requires special processing on both the PC client and on the iSeries server. The
extra processing is not large, but their use can be problematic. Whenever possible, you
should also avoid using the three codepoints reserved as alphabetic characters for
national languages (#, @, and $ in the United States). These three codepoints should be
avoided because they represent different characters, depending on the CCSID.
Use try/catch blocks to catch exceptions that could occur in your application. This is
especially important in places where object cleanup must occur in case of an error.
Before calling any Get method of a DataReader (for example, GetiDB2String) for a column
that could contain a null value, call the IsDBNull method to check the column for a null
value. If IsDBNull returns true, the column is null and you should not call any Get method,
or an exception will result.
4.9 Migrating from ADO and OLE DB to ADO.NET
The ADO.NET architecture is different from other database provider architectures such as
ADO, OLE DB, ODBC, and JDBC. Complete coverage of the differences between these
database access technologies is beyond the scope of this book, but in this section we discuss
some of these differences and show how some of the iSeries Access for Windows OLE DB
provider functions previously accessed through Visual Basic and ADO can be performed
using ADO.NET with the IBM.Data.DB2.iSeries .NET provider.