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

首頁 > 編程 > C# > 正文

c#幾種數據庫的大數據批量插入(SqlServer、Oracle、SQLite和MySql)

2020-01-24 00:57:05
字體:
來源:轉載
供稿:網友

在之前只知道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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩有码在线视频| 久久精品成人一区二区三区| 91亚洲一区精品| 国内精品模特av私拍在线观看| www高清在线视频日韩欧美| 欧美午夜丰满在线18影院| 国产成人aa精品一区在线播放| 久久精品国产亚洲一区二区| 亚洲欧美一区二区三区情侣bbw| 在线播放亚洲激情| 最近日韩中文字幕中文| 国产v综合v亚洲欧美久久| 中文字幕日韩视频| 国产精品视频精品视频| 中文字幕亚洲欧美一区二区三区| 欧美精品videossex性护士| 亚洲欧美国产日韩天堂区| 尤物yw午夜国产精品视频明星| 久久精品视频在线| 欧美裸体男粗大视频在线观看| 青草成人免费视频| 日韩av电影在线网| 日韩最新中文字幕电影免费看| 中文字幕九色91在线| 国产日韩欧美在线视频观看| 亚洲国产成人精品电影| 久久久久久美女| 超碰日本道色综合久久综合| 在线观看欧美日韩国产| 日韩美女av在线免费观看| 日日骚久久av| 久久精品一偷一偷国产| 91人成网站www| 欧美老妇交乱视频| 久久天天躁狠狠躁夜夜躁2014| 亚洲第一av网站| 亚洲精品免费在线视频| 91系列在线播放| 51久久精品夜色国产麻豆| 亚洲网站在线观看| 中文字幕日韩av电影| 国产精品视频一区二区三区四| 亚洲日韩中文字幕| 久久综合久久美利坚合众国| 国产又爽又黄的激情精品视频| 国产成人精品日本亚洲| 国产精品视频一区二区三区四| 国产精品第三页| 欧美日韩国产在线播放| 久久久亚洲精选| 国产成人欧美在线观看| 欧美乱人伦中文字幕在线| 亚洲精品电影网在线观看| 亚洲人成在线播放| 成人av资源在线播放| 91精品国产综合久久香蕉| 久久精品国产久精国产思思| 欧美激情视频一区二区三区不卡| 久久久精品2019中文字幕神马| **欧美日韩vr在线| 久久亚洲国产精品| 国产精品99久久久久久久久久久久| 欧美成aaa人片免费看| 欧美精品久久久久| 久久999免费视频| 久久精品国产欧美亚洲人人爽| 日韩在线高清视频| 国内精品久久久久伊人av| 中文字幕日韩在线视频| www.美女亚洲精品| 色吧影院999| 亚洲视频在线播放| 播播国产欧美激情| 成人有码视频在线播放| 国产69精品99久久久久久宅男| 欧美电影免费看| 国产精品久久久久久久7电影| 日韩精品免费视频| 国产日韩欧美91| 国产91精品久| 69久久夜色精品国产7777| 97久久超碰福利国产精品…| 欧美有码在线观看视频| 红桃视频成人在线观看| 亚洲天堂男人的天堂| 亚洲肉体裸体xxxx137| 国产一区二区动漫| 亚洲福利视频免费观看| 最新的欧美黄色| 精品夜色国产国偷在线| 91免费欧美精品| 亚洲精品国产精品自产a区红杏吧| 91精品久久久久久久久久久| 久久精品视频网站| 亚洲欧洲免费视频| 亚洲精品视频在线播放| 亚洲二区在线播放视频| 一区二区三区在线播放欧美| 欧美成人自拍视频| 精品欧美激情精品一区| 亚洲91精品在线| 日韩福利伦理影院免费| 亚洲大尺度美女在线| 一本一本久久a久久精品牛牛影视| 中文字幕亚洲在线| 中文字幕欧美国内| 国产精品高潮视频| 日本国产高清不卡| 98精品国产高清在线xxxx天堂| 欧美日韩国产综合视频在线观看中文| 欧美高清自拍一区| 欧美午夜美女看片| 全色精品综合影院| 亚洲色图50p| 91国产高清在线| 欧美成人精品不卡视频在线观看| 91最新在线免费观看| 九九九久久久久久| 亚洲影院色无极综合| 久久久久久久国产精品| 欧美成人免费一级人片100| 国产精品无码专区在线观看| 久久人人97超碰精品888| 国产亚洲在线播放| 中文字幕在线观看亚洲| 2021国产精品视频| 国产美女直播视频一区| 粉嫩老牛aⅴ一区二区三区| 成人精品福利视频| 久久大大胆人体| 国产精品免费一区豆花| 亚洲偷熟乱区亚洲香蕉av| 国产精自产拍久久久久久蜜| 久久精品久久久久久国产 免费| 亚洲综合大片69999| 亚洲天堂一区二区三区| 福利视频导航一区| 国产不卡av在线| 91精品视频专区| 日韩高清免费观看| 日本高清+成人网在线观看| 精品女同一区二区三区在线播放| 欧美精品在线免费播放| 97人人做人人爱| 国产精品一区专区欧美日韩| 黑人极品videos精品欧美裸| 中文字幕欧美精品日韩中文字幕| 欧美老女人www| 亚洲成avwww人| 欧美精品久久久久久久免费观看| 国内免费精品永久在线视频| 日韩二区三区在线| 国产精品成人一区二区三区吃奶| 日韩久久免费电影| 欧美电影电视剧在线观看| 日韩中文字幕在线| 性欧美视频videos6一9| 欧美在线xxx| 国产成人综合一区二区三区| 欧美有码在线视频| 隔壁老王国产在线精品| 亚洲人成电影网站色…| 欧美视频在线观看免费网址| 影音先锋欧美在线资源|