博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Bulk Operations Using Oracle Data Provider for .NET (ODP.NET) 转载
阅读量:4337 次
发布时间:2019-06-07

本文共 9858 字,大约阅读时间需要 32 分钟。

Published: 31 Oct 2007

By: 

Chinh Do describes techniques to improve application performance with bulk operations using ODP.NET.

Introduction

In a typical multi-tier application, one of the biggest performance bottlenecks is the overhead of making round-trips to the database. Minimizing these round-trips is often the first area you should look at during performance tuning. Fortunately, the Oracle Data Provider for .NET (ODP.NET) makes it fairly easy to do this by providing several built-in methods to write and read data in bulk.

To run the code samples in this article, you will need to have:

  • ODP.NET 2.0 (download from ).
  • A table named "BULK_TEST".

Here's the script that creates the table BULK_TEST:

1.
CREATE TABLE BULK_TEST
2.
(
3.
EMPLOYEE_ID  NUMBER(10)                       NOT NULL,
4.
FIRST_NAME   VARCHAR2(64 BYTE)                NOT NULL,
5.
LAST_NAME    VARCHAR2(64 BYTE)                NOT NULL,
6.
DOB          DATE                             NOT NULL
7.
);

Bulk Inserts Using Array Binding

The Array Binding feature in ODP.NET allows you to insert multiple records in one database call. To use Array Binding, you simply setOracleCommand.ArrayBindCount to the number of records to be inserted, and pass arrays of values as parameters instead of single values:

01.
string sql =
02.
"insert into bulk_test (employee_id, first_name, last_name, dob) "
03.
"values (:employee_id, :first_name, :last_name, :dob)";
04.
 
05.
OracleConnection cnn = new OracleConnection(connectString);
06.
cnn.Open();
07.
OracleCommand cmd = cnn.CreateCommand();
08.
cmd.CommandText = sql;
09.
cmd.CommandType = CommandType.Text;
10.
cmd.BindByName = true;
11.
 
12.
// To use ArrayBinding, we need to set ArrayBindCount
13.
cmd.ArrayBindCount = numRecords;
14.
 
15.
// Instead of single values, we pass arrays of values as parameters
16.
cmd.Parameters.Add(":employee_id", OracleDbType.Int32,
17.
employeeIds, ParameterDirection.Input);
18.
cmd.Parameters.Add(":first_name", OracleDbType.Varchar2,
19.
firstNames, ParameterDirection.Input);
20.
cmd.Parameters.Add(":last_name", OracleDbType.Varchar2,
21.
lastNames, ParameterDirection.Input);
22.
cmd.Parameters.Add(":dob", OracleDbType.Date,
23.
dobs, ParameterDirection.Input);
24.
cmd.ExecuteNonQuery();
25.
cnn.Close();

As you can see, the code does not look that much different from doing a regular single-record insert. However, the performance improvement is quite drastic, depending on the number of records involved. The more records you have to insert, the bigger the performance gain. On my  PC, inserting 1,000 records using Array Binding is 90 times faster than inserting the records one at a time. Yes, you read that right: 90 times faster! Your results will vary, depending on the record size and network speed/bandwidth to the database server.

A bit of investigative work reveals that the SQL is considered to be "executed" multiple times on the server side. The evidence comes from V$SQL (look at the EXECUTIONS column). However, from the .NET point of view, everything was done in one call.

Bulk Inserts Using PL/SQL Associative Arrays

PL/SQL Associative Arrays (formerly PL/SQL Index-By Tables) allow .NET code to pass arrays as parameters to PL/SQL code (stored procedure or anonymous PL/SQL blocks). Once the arrays are in PL/SQL, you are free to use them in whichever way you wish, including turning around and inserting them into a table using the "forall" bulk bind syntax.

Why would you want to insert bulk records using PL/SQL Associative Arrays instead of the simple syntax of Array Binding? Here are a few possible reasons:

  • You need to perform additional work in PL/SQL, in addition to the bulk insert.
  • The application login does not have permission to perform the work, but you can grant the necessary privilege to a stored procedure.

The major drawback with using Associative Arrays is that you have to write PL/SQL code. I have nothing against PL/SQL, but it's not part of the skill set of the typical .NET developer. To most .NET developers, PL/SQL will be harder to write and maintain, so you will have to weigh this drawback against the potential gain in performance.

In the following example, we use PL/SQL Associative Arrays to insert 1,000 records, and returning a Ref Cursor at the end. As you can see, there's quite a bit of more code to write:

01.
OracleConnection cnn = new OracleConnection(connectString);
02.
cnn.Open();
03.
OracleCommand cmd = cnn.CreateCommand();
04.
string sql = "declare "
05.
"type t_emp_id is table of bulk_test.employee_id%type index by pls_integer; "
06.
"type t_first_name is table of bulk_test.first_name%type index by pls_integer; "
07.
"type t_last_name is table of bulk_test.last_name%type index by pls_integer; "
08.
"type t_dob is table of bulk_test.dob%type index by pls_integer; "
09.
"p_emp_id t_emp_id; "
10.
"p_first_name t_first_name; "
11.
"p_last_name t_last_name; "
12.
"p_dob t_dob; "
13.
"begin "
14.
"  p_emp_id := :emp_id; "
15.
"  p_first_name := :first_name; "
16.
"  p_last_name := :last_name; "
17.
"  p_dob := :dob; "
18.
"  forall i in p_emp_id.first..p_emp_id.last "
19.
"    insert into bulk_test (employee_id, first_name, last_name, dob) "
20.
"    values (p_emp_id(i), p_first_name(i) , p_last_name(i), p_dob(i)); "
21.
"  open :c1 for "
22.
"  select employee_id, first_name, last_name, dob from bulk_test;"
23.
"end;";
24.
cmd.CommandText = sql;
25.
 
26.
OracleParameter pEmpId = new OracleParameter(":emp_id",
27.
OracleDbType.Int32,
28.
numRecords, ParameterDirection.Input);
29.
pEmpId.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
30.
pEmpId.Value = employeeIds;
31.
 
32.
OracleParameter pFirstName = new OracleParameter(":first_name",
33.
OracleDbType.Varchar2, numRecords,
34.
ParameterDirection.Input);
35.
pFirstName.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
36.
pFirstName.Value = firstNames;
37.
 
38.
OracleParameter pLastName = new OracleParameter(":last_name",
39.
OracleDbType.Varchar2, numRecords,
40.
ParameterDirection.Input);
41.
pLastName.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
42.
pLastName.Value = lastNames;
43.
 
44.
OracleParameter pDob = new OracleParameter(":dob",
45.
OracleDbType.Date, numRecords,
46.
ParameterDirection.Input);
47.
pDob.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
48.
pDob.Value = dobs;
49.
 
50.
OracleParameter pRefCursor = new OracleParameter();
51.
pRefCursor.OracleDbType = OracleDbType.RefCursor;
52.
pRefCursor.Direction = ParameterDirection.ReturnValue;
53.
 
54.
cmd.Parameters.Add(pEmpId);
55.
cmd.Parameters.Add(pFirstName);
56.
cmd.Parameters.Add(pLastName);
57.
cmd.Parameters.Add(pDob);
58.
cmd.Parameters.Add(pRefCursor);
59.
 
60.
int rows = cmd.ExecuteNonQuery();
61.
 
62.
OracleDataReader dr = ((OracleRefCursor) pRefCursor.Value).GetDataReader();           
63.
while (dr.Read())
64.
{
65.
// Process cursor
66.
}
67.
cnn.Close();

Ref Cursors

ODP.NET Ref Cursors are objects that point to Oracle server-side cursors (or result sets). The important thing to .NET developers is that a Ref Cursor can be converted to the familiar OracleDataReader. With Ref Cursors, the logic to open result sets can be written entirely in PL/SQL and the results returned to .NET via Ref Cursors.

Why would you want to use Ref Cursors, instead of just doing an ExecuteReader with a SELECT statement? Here are some possible reasons:

  • You need to perform additional work in PL/SQL before returning the result set(s).
  • The user does not have direct access to the table(s) in question.

Here is a real world example. Say you need to update a record in the Orders table and insert a new record into the OrdersAudit table at the same time. Instead of executing two database calls, you can wrap everything into an anonymous PL/SQL block and make one database call.

See the previous section for code example using a Ref Cursor.

Controlling FetchSize

Controlling the FetchSize property is another method to minimize server round-trips. When you read data from the  using the OracleDataReader object, ODP.NET retrieves the data for you in chunks behind the scene. By default, a 64K chunk of data is retrieved each time. However, you can change the chunk size by setting the FetchSize property. By increasing FetchSize, you will lower the number of data retrieval round-trips and increase the overall retrieval operation.

It's typical to set the FetchSize property is to the RowSize (of the OracleCommand object) multiplied by the number of records expected:

1.
OracleDataReader dr = cmd.ExecuteReader();
2.
dr.FetchSize = cmd.RowSize * numRecords;
3.
while (dr.Read())
4.
{
5.
// Perform reads...
6.
}

When working with a Ref Cursor, the OracleCommand.RowSize property is always zero. You either have to calculate the row size at design time or use reflection at run-time to determine the RowSize by looking at the instance variable m_rowSize on the OracleDataReader object:

1.
FieldInfo fi = dr.GetType().GetField("m_rowSize"
2.
, BindingFlags.Instance | BindingFlags.NonPublic);
3.
int rowSize = (int) fi.GetValue(dr);

On my development PC, when reading 10,000 records from BULK_TEST, setting FetchSize = RowSize * <total number of records> improves the total elapsed time by a factor of two over leaving FetchSize at the default value (985 vs. 407 milliseconds).

Notes

For clarity purposes, the example code in this article does not use the "using" pattern for IDisposable objects. It's recommended that you always use "using" with IDisposable objects such as OracleConnection or OracleCommand.

Summary

In this article, we looked at how various bulk operations in ODP.NET 2.0 can help you improve the performance of your ODP.NET application. Being familiar with these techniques can help you plan, design and implement applications that meet performance goals.

References

转载于:https://www.cnblogs.com/asingna/archive/2010/12/08/1900286.html

你可能感兴趣的文章
MFC接收ShellExecute多个参数
查看>>
volatile和synchronized的区别
查看>>
RocketMQ介绍与云服务器安装
查看>>
Jenkins插件HTML Publisher Plugin的使用
查看>>
A. The number of positions
查看>>
Windows中cmd的DOS命令查看占用某端口的程序及PID号
查看>>
设计多列布局
查看>>
返回一个整数数组中最大子数组的和
查看>>
解决FLASH遮住层的问题 IE,Firefox都适用!
查看>>
第七章:线程以及线程间的通信
查看>>
算法 PK 猫咪 | 章鱼保罗后继竟然是只猫?
查看>>
读书报告汇总
查看>>
rsync+inotify
查看>>
linux 创建连接命令 ln -s 软链接
查看>>
数据源与连接池
查看>>
开源工作流系统
查看>>
Provider 模式
查看>>
系统分析与建模6
查看>>
window.onload起冲突解决办法
查看>>
并发量计算研究
查看>>