Skip to Main Content
  • Questions
  • Performance Issue : Oracle Dot Net Array Binding

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Praveen.

Asked: April 14, 2021 - 1:57 pm UTC

Last updated: April 21, 2021 - 4:45 am UTC

Version: Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production, PL/SQL Release 12.1.0.2.0 - Production, Core 12.1.0.2.0 Production, TNS for Linux: Version 12.1.0.2.0 - Production, NLSRTL Version 12.1.0.2.0 - Production

Viewed 100+ times

You Asked

Hi Tom / Team,

I am connecting to the oracle server through dot net Oracle Data Provider dll Oracle.DataAccess(V 4.112.3.0) where I make a stored procedure call which essentially makes a database insert to a table and returns the id of newly created record using the sequence as out parameter. Here I am making batch execution of bulk records e.g. 500 records at a time through oracle array binding.

Actually, our server is sitting in Newyork and we connect to the server from different region using ODP dot net, so if NY user connects to server from the same region it is superfast like 300ms for batch execution of 500 and when I connect to the same server from Glasgow UK, it takes around 65 sec or so to execute same batch size, I checked from the different region as well but it took more time than NY. Additionally, I tried some random checks by removing out parameter from the stored procedure, At that time it was superfast like 500 ms obviously not as fast as the NY region as there are some additional network hops to the server. Here one thing that is different between the NY guy and me is he has vdi machine wired to the intranet and I have a laptop connecting to the intranet using cisco VPN.

I took a look to understand Array Binding in depth what I understood is Array Binding creates a batch of records and execute them together within a single connection session, it works exactly the same but not in the case of out parameter, I guess internally it is waiting for something to return on each record execution but if this is the case then why it is superfast in NY region? Below are my questions.
* Why array binding works differently in two different regions?
* How it works internally when we return something from PL/SQL procedure/function?
* What is the best way to improve performance when we connect through the different regions(other than server hosting region)?
below are my dot net and PL/SQL code.

C# Code :
class Program
{
 static void Main(string[] args)
 {
  Stopwatch sw = new Stopwatch();
  OracleConnection cnn = new OracleConnection("user id=xxxx;data source=xxxx;password=xxxx");
  cnn.Open();
  try
  {
   OracleCommand cmd = cnn.CreateCommand();
   cmd.CommandText = "InsertCompanyInfoProc";
   cmd.CommandType = CommandType.StoredProcedure;
   cmd.BindByName = true;

   string[] companyNames, addresses1, addresses2, cities, states, zips, countries;
   int[] output;
   int arrSize = 500; 
   companyNames = new string[arrSize];
   addresses1 = new string[arrSize];
   addresses2 = new string[arrSize];
   cities = new string[arrSize];
   states = new string[arrSize];
   zips = new string[arrSize];
   countries = new string[arrSize];
   output = new int[arrSize];

   for (int i = 0; i < arrSize; i++)
   {
    int companyId = i+1;
    companyNames[i] = "Demo Company " + companyId;
    addresses1[i] = "123 West Washington";
    addresses2[i] = "Suite 200";
    cities[i] = "Chicago";
    states[i] = "IL";
    zips[i] = "60606";
    countries[i] = "USA";
   }

   //To use ArrayBinding, we need to set ArrayBindCount
   cmd.ArrayBindCount = arrSize;

   //Instead of single value, we pass array of values as parameters
   cmd.Parameters.Add("prm_CompanyName", OracleDbType.Varchar2, companyNames, ParameterDirection.Input);
   cmd.Parameters.Add("prm_Address1", OracleDbType.Varchar2, addresses1, ParameterDirection.Input);
   cmd.Parameters.Add("prm_Address2", OracleDbType.Varchar2, addresses2, ParameterDirection.Input);
   cmd.Parameters.Add("prm_City", OracleDbType.Varchar2, cities, ParameterDirection.Input);
   cmd.Parameters.Add("prm_State", OracleDbType.Varchar2, states, ParameterDirection.Input);
   cmd.Parameters.Add("prm_Zip", OracleDbType.Varchar2, zips, ParameterDirection.Input);
   cmd.Parameters.Add("prm_Country", OracleDbType.Varchar2, countries, ParameterDirection.Input);
   cmd.Parameters.Add("prm_CompanyId", OracleDbType.Int32, output, ParameterDirection.Output);
   
   sw.Start();
   cmd.ExecuteNonQuery();
   sw.Stop();

   Console.WriteLine("Total Elapsed Time: " + sw.ElapsedMilliseconds);
   Console.ReadKey();
  }
  catch(Exception e)
  {
   Console.WriteLine(e.Message);
  }
  finally
  {
   cnn.Close();
  }
 }
}



PL/SQL Code :
CREATE OR REPLACE PROCEDURE InsertCompanyInfoProc(
 prm_CompanyName COMPANY.COMPANY_NAME%TYPE,
 prm_Address1 COMPANY.ADDRESS1%TYPE,
 prm_Address2 COMPANY.ADDRESS2%TYPE,
 prm_City COMPANY.CITY%TYPE,
 prm_State COMPANY.STATE%TYPE,
 prm_Zip COMPANY.ZIP%TYPE,
 prm_Country COMPANY.COUNTRY%TYPE,
 prm_CompanyId OUT COMPANY.COMPANY_ID%TYPE
)
IS

BEGIN
 SELECT bulk_test_seq.nextval
 INTO prm_CompanyId
 FROM DUAL;

 INSERT INTO COMPANY
 (
  COMPANY_ID,
  COMPANY_NAME,
  ADDRESS1,
  ADDRESS2,
  CITY,
  STATE,
  ZIP,
  COUNTRY
 )
 VALUES
 (
  prm_CompanyId,
  prm_CompanyName,
  prm_Address1,
  prm_Address2,
  prm_City,
  prm_State,
  prm_Zip,
  prm_Country
 );
END InsertCompanyInfoProc;


CREATE TABLE "MASTER"."COMPANY"
(
 "COMPANY_ID" NUMBER(10,0) NOT NULL ENABLE,
 "COMPANY_NAME" VARCHAR2(50 BYTE) NOT NULL ENABLE,
 "ADDRESS1" VARCHAR2(100 BYTE) NOT NULL ENABLE,
 "ADDRESS2" VARCHAR2(100 BYTE) NOT NULL ENABLE,
 "CITY" VARCHAR2(20 BYTE) NOT NULL ENABLE,
 "STATE" VARCHAR2(20 BYTE) NOT NULL ENABLE,
 "ZIP" VARCHAR2(10 BYTE) NOT NULL ENABLE,
 "COUNTRY" VARCHAR2(20 BYTE) NOT NULL ENABLE,
 CONSTRAINT "COMPANY_PK" PRIMARY KEY("COMPANY_ID")
)


CREATE SEQUENCE "MASTER"."BULK_TEST_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 
 INCREMENT BY 1 
 START WITH 1 
 CACHE 20 
 NOORDER NOCYCLE NOPARTITION;

and we said...

I'm not an expert in dot net, but I'm pretty sure you are not array binding here if that is indeed the PLSQL procedure you are calling, because it is only taking scalar arguments (not arrays).

Typically array binding is going to be either done with the INSERT on the client side, eg

cmd.CommandText =  "insert into 
  jobs (job_id, " +
        "job_title, " +
        "min_salary, " +
        "max_salary) " +
        "values (:1, :2, :3, :4)";
// set the number of elements 
// in the arrays and all three 
// arrays are the same size
cmd.ArrayBindCount = job_id_vals.Length;
// add parameters to collection
cmd.Parameters.Add(p_job_id);
cmd.Parameters.Add(p_job_title);
cmd.Parameters.Add(p_min_salary);
cmd.Parameters.Add(p_max_salary);
// perform the array insert in 
// a single call
cmd.ExecuteNonQuery();


or you will create the PLSQL procedure to accept an array and you will do the array processing *inside* the PLSQL procedure, eg

CREATE OR REPLACE PROCEDURE InsertCompanyInfoProc(
 prm_CompanyName varchar2_array_type
 prm_Address1 varchar2_array_type
 prm_Address2 varchar2_array_type
 prm_City varchar2_array_type
 prm_State varchar2_array_type
 prm_Zip varchar2_array_type
 prm_Country varchar2_array_type
 prm_CompanyId OUT number_array_type
)
IS

BEGIN
 forall i in 1 .. prm_CompanyName.count
   INSERT INTO COMPANY
   (
    COMPANY_ID,
    COMPANY_NAME,
    ADDRESS1,
    ADDRESS2,
    CITY,
    STATE,
    ZIP,
    COUNTRY
   )
   VALUES
   (
    bulk_test_seq.nextval,
    prm_CompanyName(i),
    prm_Address1(i),
    prm_Address2(i),
    prm_City(i),
    prm_State(i),
    prm_Zip(i),
    prm_Country(i)
   )
  returning COMPANY_ID bulk collect into prm_CompanyId;
  
END InsertCompanyInfoProc;


and you make a single call to that proc with the populated arrays.

I'm *suspect* what is happening here is we're calling the procedure once *per* array row, hence you'd be getting a massive latency hit.