C#实现Oracle批量写入数据的方法详解

 更新时间:2022年11月01日 08:53:46   作者:Csharp小记  
往数据库批量写入数据,这个功能使用频率相对还是比较高的,特别是在做一些导入等功能的时候。本文为大家介绍了C#实现Oracle批量写入数据的方法,需要的可以参考一下

文章描述

往数据库批量写入数据,这个功能使用频率相对还是比较高的,特别是在做一些导入等功能的时候。net的程序大部分都是使用的sqlserver或者mysql数据库,oracle相对少一些。不过说到Oracle批量写入数据,我只想吐槽一句: .Net苦Oracle久矣。

由于一直使用的是.Net Framework,所以我感觉Oracle在批量写入这一块很不友好。之前有使用过两种方式,但是弊端太明显。分别是: OracleDataAdapter.Update(DataTable dataTable)Oracle.DataAccess.Client下的OracleBulkCopy,以下简单说下:

第一种感觉就是只是提供了一个批量提交的方式,在效率方面,并没有什么提升;

第二种对Oracle环境配置的什么的有要求,所以我在使用的时候,把类似精简oracle的一些文件放了进去(可能是这个原因),在初始化和Open的时候依然会卡顿一下。即便如此,这个方法依然很快。但是弊端基本无解,如非无奈,尽量不要使用(但是我们确实是无奈之举,所以依然使用了一段时间,并采用了以下方式尽量避免这个问题,采用的方式是:批量写入一个无主键的临时表,然后把这个临时表在业务、事务中使用)。

弊端1:没有事务,只有一个内部事务(UseInternalTransaction),单纯的用来保证此次提交数据的一次性而已。

弊端2:会破坏主键,即便数据主键重复依然可以写入成功。导致表结构混乱,引发一系列问题!!!

之前有在SqlSugar中看到Oracle的批量提交,他在备注有表明以上弊端,但是奇怪的是他同时标注了只支持.Net Core,但其实.Net Framework也是可以用。而且我没明白既然只支持.Net Core,为什么不用我下面要写的第三种方式

再然后偶尔在网上发现了第三种方式: .Net Core下,基于Oracle.ManagedDataAccess.Client中的ArrayBindCount,测试后发现,无上述弊端,效率比OracleBulkCopy更优秀。但是如果要继承到老项目中的话,建议写个插件或者Web Api来处理

开发环境

.NET Framework4.5、.NET Core 3.1

开发工具

Visual Studio 2019

实现代码

//OracleDataAdapter.Update(DataTable dataTable)方式
using System.Data.OracleClient;
public static int BulkCopy(DataTable dataTable) {
            int result = 0;


            List<string> sql_column = new List<string>();
            List<string> sql_para = new List<string>();

            List<OracleParameter> paras = new List<OracleParameter>();
            foreach(DataColumn column in dataTable.Columns) {
                sql_column.Add(column.ColumnName);
                sql_para.Add(":" + column.ColumnName);

                OracleParameter para = new OracleParameter(column.ColumnName, ConvertOracleDbType(column.DataType));
                para.SourceColumn = column.ColumnName;
                paras.Add(para);
            }

            using(OracleConnection conn = new OracleConnection(ConfigurationManager.ConnectionStrings["default"].ConnectionString)) {
                conn.Open();
                string sql = $"insert into {dataTable.TableName}({string.Join(",", sql_column)}) values ({string.Join(",", sql_para)})";
                OracleCommand cmd = new OracleCommand(sql, conn);
                cmd.Parameters.AddRange(paras.ToArray());
                OracleDataAdapter adapter = new OracleDataAdapter();
                adapter.InsertCommand = cmd;
                result = adapter.Update(dataTable);
                conn.Close();
            }
            return result;
        }

        public static OracleType ConvertOracleDbType(Type type) {
            switch(type.Name.ToLower()) {
                case "decimal":
                return OracleType.Number;
                case "string":
                return OracleType.VarChar;
                case "datetime":
                return OracleType.DateTime;
                default:
                return OracleType.VarChar;
            }
        }
//OracleBulkCopy
using Oracle.DataAccess.Client;
public static int BulkCopy(DataTable dataTable) {

            int result = 0;

            using(OracleConnection conn = new OracleConnection(ConfigurationManager.ConnectionStrings["default"].ConnectionString)) {
                conn.Open();
                OracleBulkCopy oracleBulkCopy = new OracleBulkCopy(conn, OracleBulkCopyOptions.UseInternalTransaction);
                oracleBulkCopy.DestinationTableName = dataTable.TableName;

                foreach(DataColumn column in dataTable.Columns) {
                    oracleBulkCopy.ColumnMappings.Add(new OracleBulkCopyColumnMapping(column.ColumnName, column.ColumnName));
                }

                oracleBulkCopy.WriteToServer(dataTable);
                conn.Close();
            }
            return result;
        }
//ArrayBindCount
using Oracle.ManagedDataAccess.Client;
   public static OracleDbType ConvertOracleDbType(Type type) {
            switch(type.Name.ToLower()) {
                case "decimal":
                return OracleDbType.Decimal;
                case "string":
                return OracleDbType.Varchar2;
                case "datetime":
                return OracleDbType.Date;
                default:
                return OracleDbType.Varchar2;
            }
        }
        public static dynamic InitList(Type type) {
            switch(type.Name.ToLower()) {
                case "decimal":
                return new List<decimal>();
                case "string":
                return new List<string>();
                case "datetime":
                return new List<DateTime>();
                default:
                return new List<string>();
            }
        }
        public static void AddValue(dynamic list, Type type, object value) {
            switch(type.Name.ToLower()) {
                case "decimal":
                list.Add(Convert.ToDecimal(value));
                break;
                case "string":
                list.Add(Convert.ToString(value));
                break;
                case "datetime":
                list.Add(Convert.ToDateTime(value));
                break;
                default:
                list.Add(Convert.ToString(value));
                break;
            }
        }

        public static int BulkCopy(DataTable dataTable) {

            string connStr = "";
            int result = 0;

            List<string> sql_column = new List<string>();
            List<string> sql_para = new List<string>();

            List<OracleParameter> paras = new List<OracleParameter>();
            foreach(DataColumn column in dataTable.Columns) {

                sql_column.Add(column.ColumnName);
                sql_para.Add(":" + column.ColumnName);

                dynamic list = InitList(column.DataType);

                foreach(DataRow dr in dataTable.Rows) {
                    AddValue(list, column.DataType, dr[column]);
                }
                OracleParameter para = new OracleParameter(column.ColumnName, ConvertOracleDbType(column.DataType));
                para.Value = list.ToArray();
                paras.Add(para);
            }
            using(var connection = new OracleConnection(connStr)) {
                connection.Open();
                string sql = $"insert into {dataTable.TableName}({string.Join(",", sql_column)}) values ({string.Join(",", sql_para)})";
                OracleCommand cmd = new OracleCommand(sql, connection);
                cmd.Parameters.AddRange(paras.ToArray());
                cmd.ArrayBindCount = dataTable.Rows.Count;
                result = cmd.ExecuteNonQuery();
                connection.Close();
            }
            return result;
        }

代码解析:连接Oracle的字符串尽量写成Data Source=IP:Port/DB;User ID=USER;password=PWD;即便配置了tns文件。

上面的方式有做了一下效率比对(各个机器配置等不一致,仅供参考):    

测试数据为5万条,3列,第一种等了很久没写完,直接断掉了;第二种和第三种都是4秒多一点。

到此这篇关于C#实现Oracle批量写入数据的方法详解的文章就介绍到这了,更多相关C# Oracle批量写入数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C#版Tesseract库的使用技巧

    C#版Tesseract库的使用技巧

    本文给大家分享C#版Tesseract库的使用技巧,在这里大家需要注意一下tesseract的识别语言包要自己下载后包含到项目里面,并设置为始终复制,或者直接把这个文件包放到运行程序目录(bin\debug)下的,具体实现代码跟随小编一起学习下吧
    2021-05-05
  • Unity中协程IEnumerator的使用方法介绍详解

    Unity中协程IEnumerator的使用方法介绍详解

    本文主要介绍了Unity中协程IEnumerator的使用方法介绍详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • c# 实现RSA非对称加密算法

    c# 实现RSA非对称加密算法

    RSA解决了对称加密的一个不足,比如AES算法加密和解密时使用的是同一个秘钥,因此这个秘钥不能公开,因此对于需要公开秘钥的场合,我们需要在加密和解密过程中使用不同的秘钥,加密使用的公钥可以公开,解密使用的私钥要保密,这就是非对称加密的好处。 
    2021-06-06
  • Unity Shader实现3D翻页效果

    Unity Shader实现3D翻页效果

    这篇文章主要为大家详细介绍了Unity Shader实现3D翻页效果,Plane实现翻页效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • C#利用Random得随机数求均值、方差、正态分布的方法

    C#利用Random得随机数求均值、方差、正态分布的方法

    这篇文章主要介绍了C#利用Random得随机数求均值、方差、正态分布的方法,涉及C#数学运算及概率统计的相关技巧,需要的朋友可以参考下
    2015-05-05
  • .net实现文件读写的几种常用方法

    .net实现文件读写的几种常用方法

    这篇文章主要介绍了.net实现文件读写的几种常用方法,非常实用,需要的朋友可以参考下
    2014-08-08
  • c#滚动字幕动画窗体制作步骤

    c#滚动字幕动画窗体制作步骤

    在本篇文章里小编给大家分享了c#滚动字幕动画窗体制作步骤和相关代码,需要的朋友们可以学习下。
    2019-02-02
  • C# 文件下载之断点续传实现代码

    C# 文件下载之断点续传实现代码

    本篇文章主要介绍了C# 文件下载之断点续传实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • WPF自定义MenuItem样式的实现方法

    WPF自定义MenuItem样式的实现方法

    这篇文章主要给大家介绍了关于WPF自定义MenuItem样式的实现方法,文中通过示例代码介绍的非常详细,对大家学习或者使用WPF具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-06-06
  • C#使用读写锁三行代码简单解决多线程并发的问题

    C#使用读写锁三行代码简单解决多线程并发的问题

    本文主要介绍了C#使用读写锁三行代码简单解决多线程并发写入文件时提示“文件正在由另一进程使用,因此该进程无法访问此文件”的问题。需要的朋友可以参考借鉴
    2016-12-12

最新评论