C#實(shí)現(xiàn)Oracle批量寫(xiě)入數(shù)據(jù)的方法詳解
文章描述
往數(shù)據(jù)庫(kù)批量寫(xiě)入數(shù)據(jù),這個(gè)功能使用頻率相對(duì)還是比較高的,特別是在做一些導(dǎo)入等功能的時(shí)候。net的程序大部分都是使用的sqlserver或者mysql數(shù)據(jù)庫(kù),oracle相對(duì)少一些。不過(guò)說(shuō)到Oracle批量寫(xiě)入數(shù)據(jù),我只想吐槽一句: .Net苦Oracle久矣。
由于一直使用的是.Net Framework,所以我感覺(jué)Oracle在批量寫(xiě)入這一塊很不友好。之前有使用過(guò)兩種方式,但是弊端太明顯。分別是: OracleDataAdapter.Update(DataTable dataTable)和Oracle.DataAccess.Client下的OracleBulkCopy,以下簡(jiǎn)單說(shuō)下:
第一種感覺(jué)就是只是提供了一個(gè)批量提交的方式,在效率方面,并沒(méi)有什么提升;
第二種對(duì)Oracle環(huán)境配置的什么的有要求,所以我在使用的時(shí)候,把類(lèi)似精簡(jiǎn)oracle的一些文件放了進(jìn)去(可能是這個(gè)原因),在初始化和Open的時(shí)候依然會(huì)卡頓一下。即便如此,這個(gè)方法依然很快。但是弊端基本無(wú)解,如非無(wú)奈,盡量不要使用(但是我們確實(shí)是無(wú)奈之舉,所以依然使用了一段時(shí)間,并采用了以下方式盡量避免這個(gè)問(wèn)題,采用的方式是:批量寫(xiě)入一個(gè)無(wú)主鍵的臨時(shí)表,然后把這個(gè)臨時(shí)表在業(yè)務(wù)、事務(wù)中使用)。
弊端1:沒(méi)有事務(wù),只有一個(gè)內(nèi)部事務(wù)(UseInternalTransaction),單純的用來(lái)保證此次提交數(shù)據(jù)的一次性而已。
弊端2:會(huì)破壞主鍵,即便數(shù)據(jù)主鍵重復(fù)依然可以寫(xiě)入成功。導(dǎo)致表結(jié)構(gòu)混亂,引發(fā)一系列問(wèn)題!??!
之前有在SqlSugar中看到Oracle的批量提交,他在備注有表明以上弊端,但是奇怪的是他同時(shí)標(biāo)注了只支持.Net Core,但其實(shí).Net Framework也是可以用。而且我沒(méi)明白既然只支持.Net Core,為什么不用我下面要寫(xiě)的第三種方式
再然后偶爾在網(wǎng)上發(fā)現(xiàn)了第三種方式: .Net Core下,基于Oracle.ManagedDataAccess.Client中的ArrayBindCount,測(cè)試后發(fā)現(xiàn),無(wú)上述弊端,效率比OracleBulkCopy更優(yōu)秀。但是如果要繼承到老項(xiàng)目中的話,建議寫(xiě)個(gè)插件或者Web Api來(lái)處理

開(kāi)發(fā)環(huán)境
.NET Framework4.5、.NET Core 3.1
開(kāi)發(fā)工具
Visual Studio 2019
實(shí)現(xiàn)代碼
//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的字符串盡量寫(xiě)成Data Source=IP:Port/DB;User ID=USER;password=PWD;即便配置了tns文件。
上面的方式有做了一下效率比對(duì)(各個(gè)機(jī)器配置等不一致,僅供參考):
測(cè)試數(shù)據(jù)為5萬(wàn)條,3列,第一種等了很久沒(méi)寫(xiě)完,直接斷掉了;第二種和第三種都是4秒多一點(diǎn)。
到此這篇關(guān)于C#實(shí)現(xiàn)Oracle批量寫(xiě)入數(shù)據(jù)的方法詳解的文章就介紹到這了,更多相關(guān)C# Oracle批量寫(xiě)入數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#框架winform實(shí)現(xiàn)簡(jiǎn)單點(diǎn)餐系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C#框架winform實(shí)現(xiàn)簡(jiǎn)單點(diǎn)餐系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
C#實(shí)現(xiàn)WPS文件轉(zhuǎn)PDF格式的方法示例
這篇文章主要介紹了C#實(shí)現(xiàn)WPS文件轉(zhuǎn)PDF格式的方法,涉及C#針對(duì)office組件的相關(guān)引用與操作技巧,需要的朋友可以參考下2017-11-11
Unity游戲開(kāi)發(fā)之射擊小游戲的實(shí)現(xiàn)
本篇文章為大家?guī)?lái)一個(gè)橫版2D射擊小游戲,游戲制作超級(jí)簡(jiǎn)單,玩法一學(xué)就會(huì)。文中的示例代碼講解詳細(xì),快跟隨小編一起動(dòng)手試一試2022-03-03
Unity?UGUI的CanvasScaler畫(huà)布縮放器組件介紹使用
這篇文章主要為大家介紹了Unity?UGUI的CanvasScaler畫(huà)布縮放器組件介紹使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
C# 利用IRawPixels接口遍歷柵格數(shù)據(jù)
本文主要介紹了利用IRawPixels接口遍歷柵格數(shù)據(jù)。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02
C#實(shí)現(xiàn)XML文件與DataTable、Dataset互轉(zhuǎn)
這篇文章介紹了C#實(shí)現(xiàn)XML文件與DataTable、Dataset互轉(zhuǎn)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04
C#查看/寫(xiě)入日志到Windows事件查看器的操作方法
Windows 操作系統(tǒng)將與計(jì)算機(jī)的系統(tǒng)性能、應(yīng)用程序和安全方面相關(guān)的每個(gè)事件記錄在 C:\WINDOWS\system32\winevt 的日志中,事件查看器從這些原始事件日志中讀取信息,然后以可讀格式呈現(xiàn)信息,本文介紹了C#如何查看/寫(xiě)入日志到Windows事件查看器,需要的朋友可以參考下2024-09-09

