如何将DataTable(或类似)与Oracle DB一起使用

Kod*_*ody 6 c# devart dapper oracledb .net-core-2.0

我最近一直在处理一些性能问题,并试图找出如何以指数方式提高某些Oracle数据库调用的性能.

技术:

我熟悉DataTableDapper和SQL Server 的使用和表值参数,并希望使用上述技术复制它.我还没有能够重现下面的解决方案来使用Devart和OracleManaged:

下面的代码不是我正在运行的......这是一个释义的例子.我只需要与Oracle一起工作的东西来传递DataTable或在查询/插入中使用的对象数组.

SQL Server:

CREATE TYPE MyCustomerInfo AS TABLE
(
    Id BIGINT NOT NULL,
    --Name NVARCHAR(32) NOT NULL,
    --...
);
Run Code Online (Sandbox Code Playgroud)

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") });
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

C#for Oracle DB:

我采用了类似于SQL Server的方法,但同时使用了Devart和OracleManaged,并且都没有工作.我也[非常不高兴]尝试使用OracleCommandOracleParameter直接 - 再次使用Devart和OracleManaged - 无济于事.

我对Devart的结果似乎表明功能是故意阻止的.我对OracleManaged的结果似乎表明它尚未实现,这并不奇怪,因为它是beta(据称今年发布的第3季度).

我的下一个方法可能是使用与Devart相关的数组(我很少有经验或渴望学习).在这一点上,我只是在探索能够以类似的性能提升工作的东西......

编辑:使用每参数数组的方法是可能的,但非常不方便,因为大类最终有12个以上的数组.我想要替代这种方法.

Bit*_*ler 1

@Kody 这篇文章有点旧,所以这可能不会帮助你,但对其他人来说这可能会有所帮助。虽然我不知道有什么方法可以将数据表/UDT 与托管 Oracle 客户端一起使用,但如果您严格只想减少数据库往返次数并通过一次调用执行一堆插入/删除/更新数据库你可以尝试这种方法:

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();
}
Run Code Online (Sandbox Code Playgroud)

不确定如果您要插入/更新数亿条记录,性能会有多好,但我已经通过插入 80K 记录进行了测试,并且我的导入功能不再需要花费几分钟的时间(之前有人将其编码为一个简单的循环,调用 insert for每条记录)缩短到几秒钟。我没有确切的数字,因为一旦导入时间缩短了近 2 个数量级,我就很高兴可以继续处理其他事情。