I've recently been dealing with some performance problems and am trying to figure out how to exponentially increase the performance of some calls to an Oracle DB.
Technologies:
I am familiar with the use of DataTable
and Table-Valued Parameters for Dapper and SQL Server and wish to replicate that with the technologies above. I have not yet been able to reproduce the solution below to work with Devart nor OracleManaged:
The code below is not what I'm running... It's a paraphrased example. I just need something that works with Oracle to pass a DataTable or array of objects to be used in a query/insert.
SQL Server:
CREATE TYPE MyCustomerInfo AS TABLE
(
Id BIGINT NOT NULL,
--Name NVARCHAR(32) NOT NULL,
--...
);
C# for SQL Server:
const string getCustomersSql = @"
SELECT
c.Id,
--c.Name
--...
FROM @myCustomers mc
LEFT JOIN Customers c
ON c.Id = mc.Id";
var myCustomers = new DataTable();
myCustomers.Columns.Add("Id", typeof(long));
//...
myCustomers.Rows.Add(1);
myCustomers.Rows.Add(2);
var customers = await sqlDbConnection.QueryAsync<Customer>(getCustomersSql, new { myCustomers = myCustomers.AsTableValuedParameter("MyCustomerInfo") });
Oracle DB (PL/SQL):
CREATE TYPE MY_CUSTOMER_INFO AS OBJECT
(
ID BIGINT,
--NAME VARCHAR2(32),
--...
);
CREATE TYPE MY_CUSTOMER_INFO_ARRAY AS TABLE OF MY_CUSTOMER_INFO;
C# for Oracle DB:
I made a similar approach as with SQL Server but with both Devart and OracleManaged and neither worked. I also [very-unhappily] tried using OracleCommand
and OracleParameter
directly - again with both Devart and OracleManaged - to no avail.
My results with Devart seem to indicate that the functionality is intentionally prevented. My results with OracleManaged seem to indicate that it hasn't been implemented yet, which isn't surprising since it's beta (supposedly released Q3 this year).
My next approach may be to use associated arrays (in which I have very little experience or desire to learn) with Devart. At this point I'm just probing for something that will work with similar performance gains...
EDIT: Using an array-per-parameter approach is possible but is awfully inconvenient as large classes end up with 12+ arrays. I'd like an alternative to this approach.
SQL*Loader is the primary method for quickly populating Oracle tables with data from external files. It has a powerful data parsing engine that puts little limitation on the format of the data in the datafile. SQL*Loader is invoked when you specify the sqlldr command or use the Enterprise Manager interface.
The most common type of table in an Oracle database is a relational table, which is structured with simple columns similar to the employees table. Two other table types are supported: object tables and XMLType tables. Any of the three table types can be defined as permanent or temporary.
Advantages of Oracle Databases The application is designed for grid computing, which uses distributed computer resources to reach a common goal. This approach delivers performance, functionality, scalability, and availability while combining unique and traditional features.
A table is the basic unit of data organization in an Oracle database. A table describes an entity, which is something of significance about which information must be recorded.
@Kody this post is a little old, so this probably won't help you but for others this may help. Although I don't know of any way to use a datatable/UDT with the Managed Oracle Client, if you strictly just want to reduce the number of round trips to the DB and perform a bunch of inserts/deletes/updates with one call to the DB you could try this approach:
using (var dbConn = ManagedOracleHelper.GetConnection())
{
dbConn.Open();
var cmd = dbConn.CreateCommand();
var udtList = GetUDTList(); // A dummy method to get a collection of Model
// objects you want to use for the bulk operation.
// This could be a dataset too, you would just need
// to change the code within the for-loop to iterate
// over rows and access the columns by name.
var firstNameArr = new string[udtList.Count];
var lastNameArr = new string[udtList.Count];
var emailArr = new string[udtList.Count];
for (var i = 0; i < udtList.Count; i++)
{
firstNameArr[i] = udtList[i].FirstName;
lastNameArr[i] = udtList[i].LastName;
emailArr[i] = udtList[i].Email;
}
cmd.CommandText = @"INSERT INTO CUSTOMERS(FIRST_NAME, LAST_NAME, EMAIL)
VALUES(:FirstName, :LastName, :Email)";;
cmd.BindByName = true;
cmd.Parameters.Add("FirstName", OracleDbType.Varchar2, ParameterDirection.Input);
cmd.Parameters.Add("LastName", OracleDbType.Varchar2, ParameterDirection.Input);
cmd.Parameters.Add("Email", OracleDbType.Varchar2, ParameterDirection.Input);
cmd.ArrayBindCount = udtList.Count;
cmd.Parameters["FirstName"].Value = firstNameArr;
cmd.Parameters["LastName"].Value = lastNameArr;
cmd.Parameters["Email"].Value = emailArr;
cmd.ExecuteNonQuery();
}
Not sure how performant this would be if you were inserting/updating hundreds of millions of records, but I have tested this with inserting 80K records and my import feature went from taking multiple minutes (previously someone coded this as a simple loop calling an insert for each record) down to a few seconds. I don't have exact numbers because once the import time went down by almost 2 orders of magnitude, I was happy enough to move on with other things.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With