亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

c#實現幾種數據庫的大數據批量插入

2019-10-29 21:15:15
字體:
來源:轉載
供稿:網友

在之前只知道SqlServer支持數據批量插入,殊不知道Oracle、SQLite和MySQL也是支持的,不過Oracle需要使用Orace.DataAccess驅動,今天就貼出幾種數據庫的批量插入解決方法。

首先說一下,IProvider里有一個用于實現批量插入的插件服務接口IBatcherProvider,此接口在前一篇文章中已經提到過了。

/// <summary>   /// 提供數據批量處理的方法。   /// </summary>   public interface IBatcherProvider : IProviderService   {     /// <summary>     /// 將 <see cref="DataTable"/> 的數據批量插入到數據庫中。     /// </summary>     /// <param name="dataTable">要批量插入的 <see cref="DataTable"/>。</param>     /// <param name="batchSize">每批次寫入的數據量。</param>     void Insert(DataTable dataTable, int batchSize = 10000);   } 

一、SqlServer數據批量插入

SqlServer的批量插入很簡單,使用SqlBulkCopy就可以,以下是該類的實現:

/// <summary>   /// 為 System.Data.SqlClient 提供的用于批量操作的方法。   /// </summary>   public sealed class MsSqlBatcher : IBatcherProvider   {     /// <summary>     /// 獲取或設置提供者服務的上下文。     /// </summary>     public ServiceContext ServiceContext { get; set; }      /// <summary>     /// 將 <see cref="DataTable"/> 的數據批量插入到數據庫中。     /// </summary>     /// <param name="dataTable">要批量插入的 <see cref="DataTable"/>。</param>     /// <param name="batchSize">每批次寫入的數據量。</param>     public void Insert(DataTable dataTable, int batchSize = 10000)     {       Checker.ArgumentNull(dataTable, "dataTable");       if (dataTable.Rows.Count == 0)       {         return;       }       using (var connection = (SqlConnection)ServiceContext.Database.CreateConnection())       {         try         {           connection.TryOpen();           //給表名加上前后導符           var tableName = DbUtility.FormatByQuote(ServiceContext.Database.Provider.GetService<ISyntaxProvider>(), dataTable.TableName);           using (var bulk = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, null)             {               DestinationTableName = tableName,                BatchSize = batchSize             })           {             //循環所有列,為bulk添加映射             dataTable.EachColumn(c => bulk.ColumnMappings.Add(c.ColumnName, c.ColumnName), c => !c.AutoIncrement);             bulk.WriteToServer(dataTable);             bulk.Close();           }         }         catch (Exception exp)         {           throw new BatcherException(exp);         }         finally         {           connection.TryClose();         }       }     }   } 

以上沒有使用事務,使用事務在性能上會有一定的影響,如果要使用事務,可以設置SqlBulkCopyOptions.UseInternalTransaction。

二、Oracle數據批量插入

System.Data.OracleClient不支持批量插入,因此只能使用Oracle.DataAccess組件來作為提供者。

/// <summary>   /// Oracle.Data.Access 組件提供的用于批量操作的方法。   /// </summary>   public sealed class OracleAccessBatcher : IBatcherProvider   {     /// <summary>     /// 獲取或設置提供者服務的上下文。     /// </summary>     public ServiceContext ServiceContext { get; set; }      /// <summary>     /// 將 <see cref="DataTable"/> 的數據批量插入到數據庫中。     /// </summary>     /// <param name="dataTable">要批量插入的 <see cref="DataTable"/>。</param>     /// <param name="batchSize">每批次寫入的數據量。</param>     public void Insert(DataTable dataTable, int batchSize = 10000)     {       Checker.ArgumentNull(dataTable, "dataTable");       if (dataTable.Rows.Count == 0)       {         return;       }       using (var connection = ServiceContext.Database.CreateConnection())       {         try         {           connection.TryOpen();           using (var command = ServiceContext.Database.Provider.DbProviderFactory.CreateCommand())           {             if (command == null)             {               throw new BatcherException(new ArgumentException("command"));             }             command.Connection = connection;             command.CommandText = GenerateInserSql(ServiceContext.Database, command, dataTable);             command.ExecuteNonQuery();           }         }         catch (Exception exp)         {           throw new BatcherException(exp);         }         finally         {           connection.TryClose();         }       }     }      /// <summary>     /// 生成插入數據的sql語句。     /// </summary>     /// <param name="database"></param>     /// <param name="command"></param>     /// <param name="table"></param>     /// <returns></returns>     private string GenerateInserSql(IDatabase database, DbCommand command, DataTable table)     {       var names = new StringBuilder();       var values = new StringBuilder();       //將一個DataTable的數據轉換為數組的數組       var data = table.ToArray();        //設置ArrayBindCount屬性       command.GetType().GetProperty("ArrayBindCount").SetValue(command, table.Rows.Count, null);        var syntax = database.Provider.GetService<ISyntaxProvider>();       for (var i = 0; i < table.Columns.Count; i++)       {         var column = table.Columns[i];          var parameter = database.Provider.DbProviderFactory.CreateParameter();         if (parameter == null)         {           continue;         }         parameter.ParameterName = column.ColumnName;         parameter.Direction = ParameterDirection.Input;         parameter.DbType = column.DataType.GetDbType();         parameter.Value = data[i];          if (names.Length > 0)         {           names.Append(",");           values.Append(",");         }         names.AppendFormat("{0}", DbUtility.FormatByQuote(syntax, column.ColumnName));         values.AppendFormat("{0}{1}", syntax.ParameterPrefix, column.ColumnName);          command.Parameters.Add(parameter);       }       return string.Format("INSERT INTO {0}({1}) VALUES ({2})", DbUtility.FormatByQuote(syntax, table.TableName), names, values);     }   } 

以上最重要的一步,就是將DataTable轉為數組的數組表示,即object[][],前數組的上標是列的個數,后數組是行的個數,因此循環Columns將后數組作為Parameter的值,也就是說,參數的值是一個數組。而insert語句與一般的插入語句沒有什么不一樣。

三、SQLite數據批量插入

SQLite的批量插入只需開啟事務就可以了,這個具體的原理不得而知。

public sealed class SQLiteBatcher : IBatcherProvider   {     /// <summary>     /// 獲取或設置提供者服務的上下文。     /// </summary>     public ServiceContext ServiceContext { get; set; }      /// <summary>     /// 將 <see cref="DataTable"/> 的數據批量插入到數據庫中。     /// </summary>     /// <param name="dataTable">要批量插入的 <see cref="DataTable"/>。</param>     /// <param name="batchSize">每批次寫入的數據量。</param>     public void Insert(DataTable dataTable, int batchSize = 10000)     {       Checker.ArgumentNull(dataTable, "dataTable");       if (dataTable.Rows.Count == 0)       {         return;       }       using (var connection = ServiceContext.Database.CreateConnection())       {         DbTransaction transcation = null;         try         {           connection.TryOpen();           transcation = connection.BeginTransaction();           using (var command = ServiceContext.Database.Provider.DbProviderFactory.CreateCommand())           {             if (command == null)             {               throw new BatcherException(new ArgumentException("command"));             }             command.Connection = connection;              command.CommandText = GenerateInserSql(ServiceContext.Database, dataTable);             if (command.CommandText == string.Empty)             {               return;             }              var flag = new AssertFlag();             dataTable.EachRow(row =>               {                 var first = flag.AssertTrue();                 ProcessCommandParameters(dataTable, command, row, first);                 command.ExecuteNonQuery();               });           }           transcation.Commit();         }         catch (Exception exp)         {           if (transcation != null)           {             transcation.Rollback();           }           throw new BatcherException(exp);         }         finally         {           connection.TryClose();         }       }     }      private void ProcessCommandParameters(DataTable dataTable, DbCommand command, DataRow row, bool first)     {       for (var c = 0; c < dataTable.Columns.Count; c++)       {         DbParameter parameter;         //首次創建參數,是為了使用緩存         if (first)         {           parameter = ServiceContext.Database.Provider.DbProviderFactory.CreateParameter();           parameter.ParameterName = dataTable.Columns[c].ColumnName;           command.Parameters.Add(parameter);         }         else         {           parameter = command.Parameters[c];         }         parameter.Value = row[c];       }     }      /// <summary>     /// 生成插入數據的sql語句。     /// </summary>     /// <param name="database"></param>     /// <param name="table"></param>     /// <returns></returns>     private string GenerateInserSql(IDatabase database, DataTable table)     {       var syntax = database.Provider.GetService<ISyntaxProvider>();       var names = new StringBuilder();       var values = new StringBuilder();       var flag = new AssertFlag();       table.EachColumn(column =>         {           if (!flag.AssertTrue())           {             names.Append(",");             values.Append(",");           }           names.Append(DbUtility.FormatByQuote(syntax, column.ColumnName));           values.AppendFormat("{0}{1}", syntax.ParameterPrefix, column.ColumnName);         });       return string.Format("INSERT INTO {0}({1}) VALUES ({2})", DbUtility.FormatByQuote(syntax, table.TableName), names, values);     }   } 

四、MySql數據批量插入

/// <summary>   /// 為 MySql.Data 組件提供的用于批量操作的方法。   /// </summary>   public sealed class MySqlBatcher : IBatcherProvider   {     /// <summary>     /// 獲取或設置提供者服務的上下文。     /// </summary>     public ServiceContext ServiceContext { get; set; }      /// <summary>     /// 將 <see cref="DataTable"/> 的數據批量插入到數據庫中。     /// </summary>     /// <param name="dataTable">要批量插入的 <see cref="DataTable"/>。</param>     /// <param name="batchSize">每批次寫入的數據量。</param>     public void Insert(DataTable dataTable, int batchSize = 10000)     {       Checker.ArgumentNull(dataTable, "dataTable");       if (dataTable.Rows.Count == 0)       {         return;       }       using (var connection = ServiceContext.Database.CreateConnection())       {         try         {           connection.TryOpen();           using (var command = ServiceContext.Database.Provider.DbProviderFactory.CreateCommand())           {             if (command == null)             {               throw new BatcherException(new ArgumentException("command"));             }             command.Connection = connection;              command.CommandText = GenerateInserSql(ServiceContext.Database, command, dataTable);             if (command.CommandText == string.Empty)             {               return;             }             command.ExecuteNonQuery();           }         }         catch (Exception exp)         {           throw new BatcherException(exp);         }         finally         {           connection.TryClose();         }       }     }      /// <summary>     /// 生成插入數據的sql語句。     /// </summary>     /// <param name="database"></param>     /// <param name="command"></param>     /// <param name="table"></param>     /// <returns></returns>     private string GenerateInserSql(IDatabase database, DbCommand command, DataTable table)     {       var names = new StringBuilder();       var values = new StringBuilder();       var types = new List<DbType>();       var count = table.Columns.Count;       var syntax = database.Provider.GetService<ISyntaxProvider>();       table.EachColumn(c =>         {           if (names.Length > 0)           {             names.Append(",");           }           names.AppendFormat("{0}", DbUtility.FormatByQuote(syntax, c.ColumnName));           types.Add(c.DataType.GetDbType());         });        var i = 0;       foreach (DataRow row in table.Rows)       {         if (i > 0)         {           values.Append(",");         }         values.Append("(");         for (var j = 0; j < count; j++)         {           if (j > 0)           {             values.Append(", ");           }           var isStrType = IsStringType(types[j]);           var parameter = CreateParameter(database.Provider, isStrType, types[j], row[j], syntax.ParameterPrefix, i, j);           if (parameter != null)           {             values.Append(parameter.ParameterName);             command.Parameters.Add(parameter);           }           else if (isStrType)           {             values.AppendFormat("'{0}'", row[j]);           }           else           {             values.Append(row[j]);           }         }         values.Append(")");         i++;       }       return string.Format("INSERT INTO {0}({1}) VALUES {2}", DbUtility.FormatByQuote(syntax, table.TableName), names, values);     }      /// <summary>     /// 判斷是否為字符串類別。     /// </summary>     /// <param name="dbType"></param>     /// <returns></returns>     private bool IsStringType(DbType dbType)     {       return dbType == DbType.AnsiString || dbType == DbType.AnsiStringFixedLength || dbType == DbType.String || dbType == DbType.StringFixedLength;     }      /// <summary>     /// 創建參數。     /// </summary>     /// <param name="provider"></param>     /// <param name="isStrType"></param>     /// <param name="dbType"></param>     /// <param name="value"></param>     /// <param name="parPrefix"></param>     /// <param name="row"></param>     /// <param name="col"></param>     /// <returns></returns>     private DbParameter CreateParameter(IProvider provider, bool isStrType, DbType dbType, object value, char parPrefix, int row, int col)     {       //如果生成全部的參數,則速度會很慢,因此,只有數據類型為字符串(包含'號)和日期型時才添加參數       if ((isStrType && value.ToString().IndexOf('/'') != -1) || dbType == DbType.DateTime)       {         var name = string.Format("{0}p_{1}_{2}", parPrefix, row, col);         var parameter = provider.DbProviderFactory.CreateParameter();         parameter.ParameterName = name;         parameter.Direction = ParameterDirection.Input;         parameter.DbType = dbType;         parameter.Value = value;         return parameter;       }       return null;     }   } 

MySql的批量插入,是將值全部寫在語句的values里,例如,insert batcher(id, name) values(1, '1', 2, '2', 3, '3', ........ 10, '10')。

五、測試

接下來寫一個測試用例來看一下使用批量插入的效果。

    public void TestBatchInsert()     {       Console.WriteLine(TimeWatcher.Watch(() =>         InvokeTest(database =>           {             var table = new DataTable("Batcher");             table.Columns.Add("Id", typeof(int));             table.Columns.Add("Name1", typeof(string));             table.Columns.Add("Name2", typeof(string));             table.Columns.Add("Name3", typeof(string));             table.Columns.Add("Name4", typeof(string));              //構造100000條數據             for (var i = 0; i < 100000; i++)             {               table.Rows.Add(i, i.ToString(), i.ToString(), i.ToString(), i.ToString());             }              //獲取 IBatcherProvider             var batcher = database.Provider.GetService<IBatcherProvider>();             if (batcher == null)             {               Console.WriteLine("不支持批量插入。");             }             else             {               batcher.Insert(table);             }              //輸出batcher表的數據量             var sql = new SqlCommand("SELECT COUNT(1) FROM Batcher");             Console.WriteLine("當前共有 {0} 條數據", database.ExecuteScalar(sql));            })));     } 

以下表中列出了四種數據庫生成10萬條數據各耗用的時間

 

數據庫

耗用時間

MsSql 00:00:02.9376300
Oracle 00:00:01.5155959
SQLite 00:00:01.6275634
MySql 00:00:05.4166891

 

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到c#教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美亚洲在线播放| 一区二区在线视频| 日韩电影免费观看在线| 日韩成人激情在线| 国产精品久久视频| 欧美丰满片xxx777| 日本一欧美一欧美一亚洲视频| 国产精品女主播| 久久99精品视频一区97| 精品久久久久久久久国产字幕| 欧美国产日韩一区二区三区| 在线精品视频视频中文字幕| 欧美激情国产精品| 亚洲网站在线看| 亚洲欧美制服第一页| 91爱爱小视频k| 欧美视频在线观看免费网址| 亚洲电影成人av99爱色| 欧美日韩亚洲系列| 国产精品一区二区久久国产| 国产精品中文久久久久久久| 久久久久久久久久久免费精品| 国产精品久久久久福利| 精品美女久久久久久免费| 欧美午夜片在线免费观看| 久久在精品线影院精品国产| 亚洲伦理中文字幕| 国产欧美韩国高清| 成人亚洲激情网| 日韩中文字幕亚洲| 欧美日韩在线观看视频| 精品日韩中文字幕| 亚洲精品97久久| 992tv成人免费视频| 欧美精品videosex牲欧美| 欧美尤物巨大精品爽| 欧美午夜激情小视频| 精品久久久中文| 亚洲激情成人网| 5566日本婷婷色中文字幕97| 国产亚洲精品va在线观看| 日韩在线视频线视频免费网站| 日韩电影中文字幕| 亚洲成av人片在线观看香蕉| 91美女片黄在线观看游戏| 91精品国产91久久久| 欧美日韩另类在线| 日本一区二区在线播放| 欧美成人中文字幕在线| 国产精品美女主播| 欧美巨猛xxxx猛交黑人97人| 精品福利一区二区| 日产日韩在线亚洲欧美| 欧美日韩电影在线观看| 97超碰蝌蚪网人人做人人爽| 亚洲免费精彩视频| 国产精品视频免费在线| 91精品啪在线观看麻豆免费| 日韩网站免费观看| 久久久最新网址| 欧美成人全部免费| 亚洲精品欧美日韩| 亚洲欧美精品suv| 久久中文字幕国产| 国产69久久精品成人看| 亚洲a成v人在线观看| 亚洲国产成人爱av在线播放| 欧美成人中文字幕在线| 国产91九色视频| 久久久久久久影院| 91av福利视频| 国产一区二区三区日韩欧美| 久久精品成人动漫| 亚洲成人激情在线| 国产精品久久久久久久久粉嫩av| 欧美成人在线网站| 久久精品国产久精国产一老狼| 日韩av日韩在线观看| 欧美精品成人在线| 久久人人爽人人爽人人片av高清| 欧美精品在线极品| 国产欧美精品一区二区| 91亚洲精品视频| 国产成人黄色av| 久久九九精品99国产精品| 国产在线观看精品一区二区三区| 国产亚洲一区精品| 欧美午夜视频一区二区| 亚洲精选一区二区| 精品国产91乱高清在线观看| xvideos成人免费中文版| 国产精品免费小视频| 国产精品爱啪在线线免费观看| 色综合视频一区中文字幕| 久久久精品电影| 国产成人精品综合久久久| 欧美国产日韩一区二区在线观看| 精品久久久久久久久久久久久久| 2019最新中文字幕| 最近2019中文字幕大全第二页| 91免费在线视频| 久久电影一区二区| 亚洲成av人乱码色午夜| 日韩精品在线观看网站| 国产一区二区日韩精品欧美精品| 欧洲亚洲妇女av| 国产精品久久久久久久av大片| 国产精品成av人在线视午夜片| 亚洲第一精品久久忘忧草社区| 国产精品美女www爽爽爽视频| 欧美大成色www永久网站婷| 亚洲日本欧美中文幕| 久久久久久香蕉网| 欧美性20hd另类| 欧美日韩国产成人高清视频| 成人精品视频99在线观看免费| 日韩精品中文字幕在线观看| 国产精品高潮在线| 日韩欧美一区二区在线| 欧美成人一区在线| 7m第一福利500精品视频| 成人福利视频在线观看| 91tv亚洲精品香蕉国产一区7ujn| 欧美综合激情网| 国产91精品久久久| 91精品国产91久久久久久不卡| 国产精品久久久久久久电影| 国内揄拍国内精品| 精品久久久久久亚洲精品| 亚洲福利视频免费观看| 最新91在线视频| 欧美成人精品一区二区三区| 久久久久久亚洲精品不卡| 日韩精品亚洲精品| 日韩欧美中文免费| 久久av红桃一区二区小说| 成人免费在线网址| 亚洲一区二区三区视频播放| 亚洲国产精品成人va在线观看| 怡红院精品视频| 久久噜噜噜精品国产亚洲综合| 日韩免费观看av| 亚洲午夜精品久久久久久久久久久久| 欧美视频在线免费看| 国产成人亚洲综合青青| 欧美成人一区二区三区电影| 国产91精品视频在线观看| 国产99久久精品一区二区 夜夜躁日日躁| 欧美精品免费看| 久久视频在线播放| 亚洲美女在线观看| 日韩在线中文视频| 久久精品国产免费观看| 亚洲小视频在线观看| 一区二区亚洲欧洲国产日韩| 精品一区精品二区| 亚洲欧美日韩直播| 日韩精品视频免费专区在线播放| 日韩欧中文字幕| 国产va免费精品高清在线| 国产一区二区三区在线播放免费观看| 国产mv免费观看入口亚洲| 亚洲日本中文字幕免费在线不卡| 日韩有码在线观看|