成人性生交大片免费看视频r_亚洲综合极品香蕉久久网_在线视频免费观看一区_亚洲精品亚洲人成人网在线播放_国产精品毛片av_久久久久国产精品www_亚洲国产一区二区三区在线播_日韩一区二区三区四区区区_亚洲精品国产无套在线观_国产免费www

主頁(yè) > 知識(shí)庫(kù) > SQL Server 批量插入數(shù)據(jù)的完美解決方案

SQL Server 批量插入數(shù)據(jù)的完美解決方案

熱門(mén)標(biāo)簽:武漢AI電銷(xiāo)機(jī)器人 地圖標(biāo)注如何弄全套標(biāo) 南京電銷(xiāo)外呼系統(tǒng)哪家好 在電子版地圖標(biāo)注要收費(fèi)嗎 電銷(xiāo)機(jī)器人 深圳 實(shí)體店地圖標(biāo)注怎么標(biāo) 外呼系統(tǒng)會(huì)封嗎 股票配資電銷(xiāo)機(jī)器人 萬(wàn)利達(dá)綜合醫(yī)院地圖標(biāo)注點(diǎn)

一、Sql Server插入方案介紹

關(guān)于 SqlServer 批量插入的方式,有三種比較常用的插入方式,Insert、BatchInsert、SqlBulkCopy,下面我們對(duì)比以下三種方案的速度

1.普通的Insert插入方法

public static void Insert(IEnumerablePerson> persons)
{
  using (var con = new SqlConnection("Server=.;Database=DemoDataBase;User ID=sa;Password=8888;"))
  {
    con.Open();
    foreach (var person in persons)
    {
      using (var com = new SqlCommand(
        "INSERT INTO dbo.Person(Id,Name,Age,CreateTime,Sex)VALUES(@Id,@Name,@Age,@CreateTime,@Sex)",
        con))
      {
        com.Parameters.AddRange(new[]
        {
          new SqlParameter("@Id", SqlDbType.BigInt) {Value = person.Id},
          new SqlParameter("@Name", SqlDbType.VarChar, 64) {Value = person.Name},
          new SqlParameter("@Age", SqlDbType.Int) {Value = person.Age},
          new SqlParameter("@CreateTime", SqlDbType.DateTime)
            {Value = person.CreateTime ?? (object) DBNull.Value},
          new SqlParameter("@Sex", SqlDbType.Int) {Value = (int)person.Sex},
        });
        com.ExecuteNonQuery();
      }
    }
  }
}

2.拼接BatchInsert插入語(yǔ)句

public static void BatchInsert(Person[] persons)
{
  using (var con = new SqlConnection("Server=.;Database=DemoDataBase;User ID=sa;Password=8888;"))
  {
    con.Open();
    var pageCount = (persons.Length - 1) / 1000 + 1;
    for (int i = 0; i  pageCount; i++)
    {
      var personList = persons.Skip(i * 1000).Take(1000).ToArray();
      var values = personList.Select(p =>
        $"({p.Id},'{p.Name}',{p.Age},{(p.CreateTime.HasValue ? $"'{p.CreateTime:yyyy-MM-dd HH:mm:ss}'" : "NULL")},{(int) p.Sex})");
      var insertSql =
        $"INSERT INTO dbo.Person(Id,Name,Age,CreateTime,Sex)VALUES{string.Join(",", values)}";
      using (var com = new SqlCommand(insertSql, con))
      {
        com.ExecuteNonQuery();
      }
    }
  }
}

3.SqlBulkCopy插入方案

public static void BulkCopy(IEnumerablePerson> persons)
{
  using (var con = new SqlConnection("Server=.;Database=DemoDataBase;User ID=sa;Password=8888;"))
  {
    con.Open();
    var table = new DataTable();
    table.Columns.AddRange(new []
    {
      new DataColumn("Id", typeof(long)), 
      new DataColumn("Name", typeof(string)), 
      new DataColumn("Age", typeof(int)), 
      new DataColumn("CreateTime", typeof(DateTime)), 
      new DataColumn("Sex", typeof(int)), 
    });
    foreach (var p in persons)
    {
      table.Rows.Add(new object[] {p.Id, p.Name, p.Age, p.CreateTime, (int) p.Sex});
    }

    using (var copy = new SqlBulkCopy(con))
    {
      copy.DestinationTableName = "Person";
      copy.WriteToServer(table);
    }
  }
}

3.三種方案速度對(duì)比

方案 數(shù)量 時(shí)間
Insert 1千條 145.4351ms
BatchInsert 1千條 103.9061ms
SqlBulkCopy 1千條 7.021ms
Insert 1萬(wàn)條 1501.326ms
BatchInsert 1萬(wàn)條 850.6274ms
SqlBulkCopy 1萬(wàn)條 30.5129ms
Insert 10萬(wàn)條 13875.4934ms
BatchInsert 10萬(wàn)條 8278.9056ms
SqlBulkCopy 10萬(wàn)條 314.8402ms

兩者插入效率對(duì)比,Insert明顯比SqlBulkCopy要慢太多,大概20~40倍性能差距,下面我們將SqlBulkCopy封裝一下,讓批量插入更加方便

二、SqlBulkCopy封裝代碼

1.方法介紹

批量插入擴(kuò)展方法簽名

方法 方法參數(shù) 介紹
BulkCopy 同步的批量插入方法
SqlConnection connection sql server 連接對(duì)象
IEnumerableT> source 需要批量插入的數(shù)據(jù)源
string tableName = null 插入表名稱(chēng)【為NULL默認(rèn)為實(shí)體名稱(chēng)】
int bulkCopyTimeout = 30 批量插入超時(shí)時(shí)間
int batchSize = 0 寫(xiě)入數(shù)據(jù)庫(kù)一批數(shù)量【如果為0代表全部一次性插入】最合適數(shù)量【這取決于您的環(huán)境,尤其是行數(shù)和網(wǎng)絡(luò)延遲。就個(gè)人而言,我將從BatchSize屬性設(shè)置為1000行開(kāi)始,然后看看其性能如何。如果可行,那么我將使行數(shù)加倍(例如增加到2000、4000等),直到性能下降或超時(shí)。否則,如果超時(shí)發(fā)生在1000,那么我將行數(shù)減少一半(例如500),直到它起作用為止?!?/td>
SqlBulkCopyOptions options = SqlBulkCopyOptions.Default 批量復(fù)制參數(shù)
SqlTransaction externalTransaction = null 執(zhí)行的事務(wù)對(duì)象
BulkCopyAsync 異步的批量插入方法
SqlConnection connection sql server 連接對(duì)象
IEnumerableT> source 需要批量插入的數(shù)據(jù)源
string tableName = null 插入表名稱(chēng)【為NULL默認(rèn)為實(shí)體名稱(chēng)】
int bulkCopyTimeout = 30 批量插入超時(shí)時(shí)間
int batchSize = 0 寫(xiě)入數(shù)據(jù)庫(kù)一批數(shù)量【如果為0代表全部一次性插入】最合適數(shù)量【這取決于您的環(huán)境,尤其是行數(shù)和網(wǎng)絡(luò)延遲。就個(gè)人而言,我將從BatchSize屬性設(shè)置為1000行開(kāi)始,然后看看其性能如何。如果可行,那么我將使行數(shù)加倍(例如增加到2000、4000等),直到性能下降或超時(shí)。否則,如果超時(shí)發(fā)生在1000,那么我將行數(shù)減少一半(例如500),直到它起作用為止?!?/td>
SqlBulkCopyOptions options = SqlBulkCopyOptions.Default 批量復(fù)制參數(shù)
SqlTransaction externalTransaction = null 執(zhí)行的事務(wù)對(duì)象

這個(gè)方法主要解決了兩個(gè)問(wèn)題:

  • 免去了手動(dòng)構(gòu)建DataTable或者IDataReader接口實(shí)現(xiàn)類(lèi),手動(dòng)構(gòu)建的轉(zhuǎn)換比較難以維護(hù),如果修改字段就得把這些地方都進(jìn)行修改,特別是還需要將枚舉類(lèi)型特殊處理,轉(zhuǎn)換成他的基礎(chǔ)類(lèi)型(默認(rèn)int
  • 不用親自創(chuàng)建SqlBulkCopy對(duì)象,和配置數(shù)據(jù)庫(kù)列的映射,和一些屬性的配置

此方案也是在我公司中使用,以滿(mǎn)足公司的批量插入數(shù)據(jù)的需求,例如第三方的對(duì)賬數(shù)據(jù)此方法使用的是Expression動(dòng)態(tài)生成數(shù)據(jù)轉(zhuǎn)換函數(shù),其效率和手寫(xiě)的原生代碼差不多,和原生手寫(xiě)代碼相比,多余的轉(zhuǎn)換損失很小【最大的性能損失都是在值類(lèi)型拆裝箱上】

此方案和其他網(wǎng)上的方案有些不同的是:不是將List先轉(zhuǎn)換成DataTable,然后寫(xiě)入SqlBulkCopy的,而是使用一個(gè)實(shí)現(xiàn)IDataReader的讀取器包裝List,每往SqlBulkCopy插入一行數(shù)據(jù)才會(huì)轉(zhuǎn)換一行數(shù)據(jù)

IDataReader方案和DataTable方案相比優(yōu)點(diǎn)

效率高:DataTable方案需要先完全轉(zhuǎn)換后,才能交由SqlBulkCopy寫(xiě)入數(shù)據(jù)庫(kù),而IDataReader方案可以邊轉(zhuǎn)換邊交給SqlBulkCopy寫(xiě)入數(shù)據(jù)庫(kù)(例如:10萬(wàn)數(shù)據(jù)插入速度可提升30%)

占用內(nèi)存少:DataTable方案需要先完全轉(zhuǎn)換后,才能交由SqlBulkCopy寫(xiě)入數(shù)據(jù)庫(kù),需要占用大量?jī)?nèi)存,而IDataReader方案可以邊轉(zhuǎn)換邊交給SqlBulkCopy寫(xiě)入數(shù)據(jù)庫(kù),無(wú)須占用過(guò)多內(nèi)存

強(qiáng)大:因?yàn)槭沁厡?xiě)入邊轉(zhuǎn)換,而且EnumerableReader傳入的是一個(gè)迭代器,可以實(shí)現(xiàn)持續(xù)插入數(shù)據(jù)的效果

2.實(shí)現(xiàn)原理

① 實(shí)體Model與表映射

數(shù)據(jù)庫(kù)表代碼

CREATE TABLE [dbo].[Person](
	[Id] [BIGINT] NOT NULL,
	[Name] [VARCHAR](64) NOT NULL,
	[Age] [INT] NOT NULL,
	[CreateTime] [DATETIME] NULL,
	[Sex] [INT] NOT NULL,
PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

實(shí)體類(lèi)代碼

public class Person
{
  public long Id { get; set; }
  public string Name { get; set; }
  public int Age { get; set; }
  public DateTime? CreateTime { get; set; }
  public Gender Sex { get; set; }
}

public enum Gender
{
  Man = 0,
  Woman = 1
}

  • 創(chuàng)建字段映射【如果沒(méi)有此字段映射會(huì)導(dǎo)致數(shù)據(jù)填錯(cuò)位置,如果類(lèi)型不對(duì)還會(huì)導(dǎo)致報(bào)錯(cuò)】【因?yàn)椋簺](méi)有此字段映射默認(rèn)是按照列序號(hào)對(duì)應(yīng)插入的】
  • 創(chuàng)建映射使用的SqlBulkCopy類(lèi)型的ColumnMappings屬性來(lái)完成,數(shù)據(jù)列與數(shù)據(jù)庫(kù)中列的映射
//創(chuàng)建批量插入對(duì)象
using (var copy = new SqlBulkCopy(connection, options, externalTransaction))
{
  foreach (var column in ModelToDataTableTModel>.Columns)
  {
    //創(chuàng)建字段映射
    copy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
  }
}

② 實(shí)體轉(zhuǎn)換成數(shù)據(jù)行

將數(shù)據(jù)轉(zhuǎn)換成數(shù)據(jù)行采用的是:反射+Expression來(lái)完成

其中反射是用于獲取編寫(xiě)Expression所需程序類(lèi),屬性等信息

其中Expression是用于生成高效轉(zhuǎn)換函數(shù)其中ModelToDataTableTModel>類(lèi)型利用了靜態(tài)泛型類(lèi)特性,實(shí)現(xiàn)泛型參數(shù)的緩存效果

ModelToDataTableTModel>的靜態(tài)構(gòu)造函數(shù)中,生成轉(zhuǎn)換函數(shù),獲取需要轉(zhuǎn)換的屬性信息,并存入靜態(tài)只讀字段中,完成緩存

③ 使用IDataReader插入數(shù)據(jù)的重載

EnumerableReader是實(shí)現(xiàn)了IDataReader接口的讀取類(lèi),用于將模型對(duì)象,在迭代器中讀取出來(lái),并轉(zhuǎn)換成數(shù)據(jù)行,可供SqlBulkCopy讀取

SqlBulkCopy只會(huì)調(diào)用三個(gè)方法:GetOrdinal、ReadGetValue

  • 其中GetOrdinal只會(huì)在首行讀取每個(gè)列所代表序號(hào)【需要填寫(xiě):SqlBulkCopy類(lèi)型的ColumnMappings屬性】
  • 其中Read方法是迭代到下一行,并調(diào)用ModelToDataTableTModel>.ToRowData.Invoke()來(lái)將模型對(duì)象轉(zhuǎn)換成數(shù)據(jù)行object[]
  • 其中GetValue方法是獲取當(dāng)前行指定下標(biāo)位置的值

3.完整代碼

擴(kuò)展方法類(lèi)

 public static class SqlConnectionExtension
  {
    /// summary>
    /// 批量復(fù)制
    /// /summary>
    /// typeparam name="TModel">插入的模型對(duì)象/typeparam>
    /// param name="source">需要批量插入的數(shù)據(jù)源/param>
    /// param name="connection">數(shù)據(jù)庫(kù)連接對(duì)象/param>
    /// param name="tableName">插入表名稱(chēng)【為NULL默認(rèn)為實(shí)體名稱(chēng)】/param>
    /// param name="bulkCopyTimeout">插入超時(shí)時(shí)間/param>
    /// param name="batchSize">寫(xiě)入數(shù)據(jù)庫(kù)一批數(shù)量【如果為0代表全部一次性插入】最合適數(shù)量【這取決于您的環(huán)境,尤其是行數(shù)和網(wǎng)絡(luò)延遲。就個(gè)人而言,我將從BatchSize屬性設(shè)置為1000行開(kāi)始,然后看看其性能如何。如果可行,那么我將使行數(shù)加倍(例如增加到2000、4000等),直到性能下降或超時(shí)。否則,如果超時(shí)發(fā)生在1000,那么我將行數(shù)減少一半(例如500),直到它起作用為止?!?param>
    /// param name="options">批量復(fù)制參數(shù)/param>
    /// param name="externalTransaction">執(zhí)行的事務(wù)對(duì)象/param>
    /// returns>插入數(shù)量/returns>
    public static int BulkCopyTModel>(this SqlConnection connection,
      IEnumerableTModel> source,
      string tableName = null,
      int bulkCopyTimeout = 30,
      int batchSize = 0,
      SqlBulkCopyOptions options = SqlBulkCopyOptions.Default,
      SqlTransaction externalTransaction = null)
    {
      //創(chuàng)建讀取器
      using (var reader = new EnumerableReaderTModel>(source))
      {
        //創(chuàng)建批量插入對(duì)象
        using (var copy = new SqlBulkCopy(connection, options, externalTransaction))
        {
          //插入的表
          copy.DestinationTableName = tableName ?? typeof(TModel).Name;
          //寫(xiě)入數(shù)據(jù)庫(kù)一批數(shù)量
          copy.BatchSize = batchSize;
          //超時(shí)時(shí)間
          copy.BulkCopyTimeout = bulkCopyTimeout;
          //創(chuàng)建字段映射【如果沒(méi)有此字段映射會(huì)導(dǎo)致數(shù)據(jù)填錯(cuò)位置,如果類(lèi)型不對(duì)還會(huì)導(dǎo)致報(bào)錯(cuò)】【因?yàn)椋簺](méi)有此字段映射默認(rèn)是按照列序號(hào)對(duì)應(yīng)插入的】
          foreach (var column in ModelToDataTableTModel>.Columns)
          {
            //創(chuàng)建字段映射
            copy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
          }
          //將數(shù)據(jù)批量寫(xiě)入數(shù)據(jù)庫(kù)
          copy.WriteToServer(reader);
          //返回插入數(shù)據(jù)數(shù)量
          return reader.Depth;
        }
      }
    }

    /// summary>
    /// 批量復(fù)制-異步
    /// /summary>
    /// typeparam name="TModel">插入的模型對(duì)象/typeparam>
    /// param name="source">需要批量插入的數(shù)據(jù)源/param>
    /// param name="connection">數(shù)據(jù)庫(kù)連接對(duì)象/param>
    /// param name="tableName">插入表名稱(chēng)【為NULL默認(rèn)為實(shí)體名稱(chēng)】/param>
    /// param name="bulkCopyTimeout">插入超時(shí)時(shí)間/param>
    /// param name="batchSize">寫(xiě)入數(shù)據(jù)庫(kù)一批數(shù)量【如果為0代表全部一次性插入】最合適數(shù)量【這取決于您的環(huán)境,尤其是行數(shù)和網(wǎng)絡(luò)延遲。就個(gè)人而言,我將從BatchSize屬性設(shè)置為1000行開(kāi)始,然后看看其性能如何。如果可行,那么我將使行數(shù)加倍(例如增加到2000、4000等),直到性能下降或超時(shí)。否則,如果超時(shí)發(fā)生在1000,那么我將行數(shù)減少一半(例如500),直到它起作用為止?!?param>
    /// param name="options">批量復(fù)制參數(shù)/param>
    /// param name="externalTransaction">執(zhí)行的事務(wù)對(duì)象/param>
    /// returns>插入數(shù)量/returns>
    public static async Taskint> BulkCopyAsyncTModel>(this SqlConnection connection,
      IEnumerableTModel> source,
      string tableName = null,
      int bulkCopyTimeout = 30,
      int batchSize = 0,
      SqlBulkCopyOptions options = SqlBulkCopyOptions.Default,
      SqlTransaction externalTransaction = null)
    {
      //創(chuàng)建讀取器
      using (var reader = new EnumerableReaderTModel>(source))
      {
        //創(chuàng)建批量插入對(duì)象
        using (var copy = new SqlBulkCopy(connection, options, externalTransaction))
        {
          //插入的表
          copy.DestinationTableName = tableName ?? typeof(TModel).Name;
          //寫(xiě)入數(shù)據(jù)庫(kù)一批數(shù)量
          copy.BatchSize = batchSize;
          //超時(shí)時(shí)間
          copy.BulkCopyTimeout = bulkCopyTimeout;
          //創(chuàng)建字段映射【如果沒(méi)有此字段映射會(huì)導(dǎo)致數(shù)據(jù)填錯(cuò)位置,如果類(lèi)型不對(duì)還會(huì)導(dǎo)致報(bào)錯(cuò)】【因?yàn)椋簺](méi)有此字段映射默認(rèn)是按照列序號(hào)對(duì)應(yīng)插入的】
          foreach (var column in ModelToDataTableTModel>.Columns)
          {
            //創(chuàng)建字段映射
            copy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
          }
          //將數(shù)據(jù)批量寫(xiě)入數(shù)據(jù)庫(kù)
          await copy.WriteToServerAsync(reader);
          //返回插入數(shù)據(jù)數(shù)量
          return reader.Depth;
        }
      }
    }
  }

封裝的迭代器數(shù)據(jù)讀取器

 /// summary>
  /// 迭代器數(shù)據(jù)讀取器
  /// /summary>
  /// typeparam name="TModel">模型類(lèi)型/typeparam>
  public class EnumerableReaderTModel> : IDataReader
  {
    /// summary>
    /// 實(shí)例化迭代器讀取對(duì)象
    /// /summary>
    /// param name="source">模型源/param>
    public EnumerableReader(IEnumerableTModel> source)
    {
      _source = source ?? throw new ArgumentNullException(nameof(source));
      _enumerable = source.GetEnumerator();
    }

    private readonly IEnumerableTModel> _source;
    private readonly IEnumeratorTModel> _enumerable;
    private object[] _currentDataRow = Array.Emptyobject>();
    private int _depth;
    private bool _release;

    public void Dispose()
    {
      _release = true;
      _enumerable.Dispose();
    }

    public int GetValues(object[] values)
    {
      if (values == null) throw new ArgumentNullException(nameof(values));
      var length = Math.Min(_currentDataRow.Length, values.Length);
      Array.Copy(_currentDataRow, values, length);
      return length;
    }

    public int GetOrdinal(string name)
    {
      for (int i = 0; i  ModelToDataTableTModel>.Columns.Count; i++)
      {
        if (ModelToDataTableTModel>.Columns[i].ColumnName == name) return i;
      }

      return -1;
    }

    public long GetBytes(int ordinal, long dataIndex, byte[] buffer, int bufferIndex, int length)
    {
      if (dataIndex  0) throw new Exception($"起始下標(biāo)不能小于0!");
      if (bufferIndex  0) throw new Exception("目標(biāo)緩沖區(qū)起始下標(biāo)不能小于0!");
      if (length  0) throw new Exception("讀取長(zhǎng)度不能小于0!");
      var numArray = (byte[])GetValue(ordinal);
      if (buffer == null) return numArray.Length;
      if (buffer.Length = bufferIndex) throw new Exception("目標(biāo)緩沖區(qū)起始下標(biāo)不能大于目標(biāo)緩沖區(qū)范圍!");
      var freeLength = Math.Min(numArray.Length - bufferIndex, length);
      if (freeLength = 0) return 0;
      Array.Copy(numArray, dataIndex, buffer, bufferIndex, length);
      return freeLength;
    }

    public long GetChars(int ordinal, long dataIndex, char[] buffer, int bufferIndex, int length)
    {
      if (dataIndex  0) throw new Exception($"起始下標(biāo)不能小于0!");
      if (bufferIndex  0) throw new Exception("目標(biāo)緩沖區(qū)起始下標(biāo)不能小于0!");
      if (length  0) throw new Exception("讀取長(zhǎng)度不能小于0!");
      var numArray = (char[])GetValue(ordinal);
      if (buffer == null) return numArray.Length;
      if (buffer.Length = bufferIndex) throw new Exception("目標(biāo)緩沖區(qū)起始下標(biāo)不能大于目標(biāo)緩沖區(qū)范圍!");
      var freeLength = Math.Min(numArray.Length - bufferIndex, length);
      if (freeLength = 0) return 0;
      Array.Copy(numArray, dataIndex, buffer, bufferIndex, length);
      return freeLength;
    }

    public bool IsDBNull(int i)
    {
      var value = GetValue(i);
      return value == null || value is DBNull;
    }
    public bool NextResult()
    {
      //移動(dòng)到下一個(gè)元素
      if (!_enumerable.MoveNext()) return false;
      //行層+1
      Interlocked.Increment(ref _depth);
      //得到數(shù)據(jù)行
      _currentDataRow = ModelToDataTableTModel>.ToRowData.Invoke(_enumerable.Current);
      return true;
    }

    public byte GetByte(int i) => (byte)GetValue(i);
    public string GetName(int i) => ModelToDataTableTModel>.Columns[i].ColumnName;
    public string GetDataTypeName(int i) => ModelToDataTableTModel>.Columns[i].DataType.Name;
    public Type GetFieldType(int i) => ModelToDataTableTModel>.Columns[i].DataType;
    public object GetValue(int i) => _currentDataRow[i];
    public bool GetBoolean(int i) => (bool)GetValue(i);
    public char GetChar(int i) => (char)GetValue(i);
    public Guid GetGuid(int i) => (Guid)GetValue(i);
    public short GetInt16(int i) => (short)GetValue(i);
    public int GetInt32(int i) => (int)GetValue(i);
    public long GetInt64(int i) => (long)GetValue(i);
    public float GetFloat(int i) => (float)GetValue(i);
    public double GetDouble(int i) => (double)GetValue(i);
    public string GetString(int i) => (string)GetValue(i);
    public decimal GetDecimal(int i) => (decimal)GetValue(i);
    public DateTime GetDateTime(int i) => (DateTime)GetValue(i);
    public IDataReader GetData(int i) => throw new NotSupportedException();
    public int FieldCount => ModelToDataTableTModel>.Columns.Count;
    public object this[int i] => GetValue(i);
    public object this[string name] => GetValue(GetOrdinal(name));
    public void Close() => Dispose();
    public DataTable GetSchemaTable() => ModelToDataTableTModel>.ToDataTable(_source);
    public bool Read() => NextResult();
    public int Depth => _depth;
    public bool IsClosed => _release;
    public int RecordsAffected => 0;
  }

模型對(duì)象轉(zhuǎn)數(shù)據(jù)行工具類(lèi)

/// summary>
  /// 對(duì)象轉(zhuǎn)換成DataTable轉(zhuǎn)換類(lèi)
  /// /summary>
  /// typeparam name="TModel">泛型類(lèi)型/typeparam>
  public static class ModelToDataTableTModel>
  {
    static ModelToDataTable()
    {
      //如果需要剔除某些列可以修改這段代碼
      var propertyList = typeof(TModel).GetProperties().Where(w => w.CanRead).ToArray();
      Columns = new ReadOnlyCollectionDataColumn>(propertyList
        .Select(pr => new DataColumn(pr.Name, GetDataType(pr.PropertyType))).ToArray());
      //生成對(duì)象轉(zhuǎn)數(shù)據(jù)行委托
      ToRowData = BuildToRowDataDelegation(typeof(TModel), propertyList);
    }

    /// summary>
    /// 構(gòu)建轉(zhuǎn)換成數(shù)據(jù)行委托
    /// /summary>
    /// param name="type">傳入類(lèi)型/param>
    /// param name="propertyList">轉(zhuǎn)換的屬性/param>
    /// returns>轉(zhuǎn)換數(shù)據(jù)行委托/returns>
    private static FuncTModel, object[]> BuildToRowDataDelegation(Type type, PropertyInfo[] propertyList)
    {
      var source = Expression.Parameter(type);
      var items = propertyList.Select(property => ConvertBindPropertyToData(source, property));
      var array = Expression.NewArrayInit(typeof(object), items);
      var lambda = Expression.LambdaFuncTModel, object[]>>(array, source);
      return lambda.Compile();
    }

    /// summary>
    /// 將屬性轉(zhuǎn)換成數(shù)據(jù)
    /// /summary>
    /// param name="source">源變量/param>
    /// param name="property">屬性信息/param>
    /// returns>獲取屬性數(shù)據(jù)表達(dá)式/returns>
    private static Expression ConvertBindPropertyToData(ParameterExpression source, PropertyInfo property)
    {
      var propertyType = property.PropertyType;
      var expression = (Expression)Expression.Property(source, property);
      if (propertyType.IsEnum)
        expression = Expression.Convert(expression, propertyType.GetEnumUnderlyingType());
      return Expression.Convert(expression, typeof(object));
    }

    /// summary>
    /// 獲取數(shù)據(jù)類(lèi)型
    /// /summary>
    /// param name="type">屬性類(lèi)型/param>
    /// returns>數(shù)據(jù)類(lèi)型/returns>
    private static Type GetDataType(Type type)
    {
      //枚舉默認(rèn)轉(zhuǎn)換成對(duì)應(yīng)的值類(lèi)型
      if (type.IsEnum)
        return type.GetEnumUnderlyingType();
      //可空類(lèi)型
      if (type.IsGenericType  type.GetGenericTypeDefinition() == typeof(Nullable>))
        return GetDataType(type.GetGenericArguments().First());
      return type;
    }

    /// summary>
    /// 列集合
    /// /summary>
    public static IReadOnlyListDataColumn> Columns { get; }

    /// summary>
    /// 對(duì)象轉(zhuǎn)數(shù)據(jù)行委托
    /// /summary>
    public static FuncTModel, object[]> ToRowData { get; }

    /// summary>
    /// 集合轉(zhuǎn)換成DataTable
    /// /summary>
    /// param name="source">集合/param>
    /// param name="tableName">表名稱(chēng)/param>
    /// returns>轉(zhuǎn)換完成的DataTable/returns>
    public static DataTable ToDataTable(IEnumerableTModel> source, string tableName = "TempTable")
    {
      //創(chuàng)建表對(duì)象
      var table = new DataTable(tableName);
      //設(shè)置列
      foreach (var dataColumn in Columns)
      {
        table.Columns.Add(new DataColumn(dataColumn.ColumnName, dataColumn.DataType));
      }

      //循環(huán)轉(zhuǎn)換每一行數(shù)據(jù)
      foreach (var item in source)
      {
        table.Rows.Add(ToRowData.Invoke(item));
      }

      //返回表對(duì)象
      return table;
    }
  }

三、測(cè)試封裝代碼

1.測(cè)試代碼

創(chuàng)表代碼

CREATE TABLE [dbo].[Person](
	[Id] [BIGINT] NOT NULL,
	[Name] [VARCHAR](64) NOT NULL,
	[Age] [INT] NOT NULL,
	[CreateTime] [DATETIME] NULL,
	[Sex] [INT] NOT NULL,
PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

實(shí)體類(lèi)代碼

定義的實(shí)體的屬性名稱(chēng)需要和SqlServer列名稱(chēng)類(lèi)型對(duì)應(yīng)

public class Person
{
  public long Id { get; set; }
  public string Name { get; set; }
  public int Age { get; set; }
  public DateTime? CreateTime { get; set; }
  public Gender Sex { get; set; }
}

public enum Gender
{
  Man = 0,
  Woman = 1
}

測(cè)試方法

//生成10萬(wàn)條數(shù)據(jù)
var persons = new Person[100000];
var random = new Random();
for (int i = 0; i  persons.Length; i++)
{
  persons[i] = new Person
  {
    Id = i + 1,
    Name = "張三" + i,
    Age = random.Next(1, 128),
    Sex = (Gender)random.Next(2),
    CreateTime = random.Next(2) == 0 ? null : (DateTime?) DateTime.Now.AddSeconds(i)
  };
}

//創(chuàng)建數(shù)據(jù)庫(kù)連接
using (var conn = new SqlConnection("Server=.;Database=DemoDataBase;User ID=sa;Password=8888;"))
{
  conn.Open();
  var sw = Stopwatch.StartNew();
  //批量插入數(shù)據(jù)
  var qty = conn.BulkCopy(persons);
  sw.Stop();
  Console.WriteLine(sw.Elapsed.TotalMilliseconds + "ms");
}

執(zhí)行批量插入結(jié)果

226.4767ms
請(qǐng)按任意鍵繼續(xù). . .

四、代碼下載

GitHub代碼地址:https://github.com/liu-zhen-liang/PackagingComponentsSet/tree/main/SqlBulkCopyComponents

您可能感興趣的文章:
  • SQLServer2008存儲(chǔ)過(guò)程實(shí)現(xiàn)數(shù)據(jù)插入與更新
  • Python實(shí)現(xiàn)讀取SQLServer數(shù)據(jù)并插入到MongoDB數(shù)據(jù)庫(kù)的方法示例
  • SQLServer中防止并發(fā)插入重復(fù)數(shù)據(jù)的方法詳解
  • 詳解C#批量插入數(shù)據(jù)到Sqlserver中的四種方式
  • SQL Server批量插入數(shù)據(jù)案例詳解

標(biāo)簽:汕頭 濟(jì)源 武威 廣東 安徽 臺(tái)州 濟(jì)寧 泰安

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《SQL Server 批量插入數(shù)據(jù)的完美解決方案》,本文關(guān)鍵詞  SQL,Server,批量,插入,數(shù)據(jù),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《SQL Server 批量插入數(shù)據(jù)的完美解決方案》相關(guān)的同類(lèi)信息!
  • 本頁(yè)收集關(guān)于SQL Server 批量插入數(shù)據(jù)的完美解決方案的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    1卡2卡3卡精品视频| 五月婷六月丁香| 国产69精品久久久久9999apgf| 亚洲欧美精品一区二区三区| 国产专区精品| 欧美成熟毛茸茸| xxxx一级片| 中文字幕一区二区三区四区视频| 天天躁日日躁狠狠躁免费麻豆| 亚洲精品tv久久久久久久久久| 成人免费视频观看视频| 一级片在线观看免费| 人狥杂交一区欧美二区| 在线中文字幕一区| 日本精品视频一区二区三区| 亚洲午夜精品久久久久久人妖| 免费无码国产精品| 国产欧美熟妇另类久久久| 夜夜春亚洲嫩草影视日日摸夜夜添夜| 欧美色999| 亚洲午夜色婷婷在线| 中文字幕日日夜夜| 一区国产精品视频| 97在线观看视频免费| 亚洲欧美中文字幕在线观看| 资源网第一页久久久| av综合网站| 精品国产一区二区三区四区阿崩| 国产aⅴ2021| 久久久久久久午夜| 一区二区三区日韩精品| 日本特级黄色大片| 国产麻豆免费| 亚洲黄色免费三级| 精品少妇一区二区三区在线| 亚洲免费av片| 日韩专区精品| 精品国产伦一区二区三区观看说明| 综合网在线视频| 日韩免费电影一区| 亚洲激情77| 欧美成人精品一区二区综合免费| 大伊人狠狠躁夜夜躁av一区| 午夜久久久久| 伊人网站在线观看| 18岁以下禁止观看的美女视频| 乱妇乱女熟妇熟女网站| 亚洲 欧美 日韩 国产综合 在线| 久久久久久久9999| 国产亚洲福利一区| 欧美调教网站| 最新国产拍偷乱拍精品| 亚洲免费大片| 亚洲成人福利片| 日本熟妇人妻xxxxx| 欧美二级三级| 91亚洲精品一区二区乱码| 国产精品久久精品| 99精品欧美一区二区| 在线观看中文| 成人性色生活片免费看爆迷你毛片| 91天堂在线| 欧美疯狂做受xxxx富婆| 国产偷人爽久久久久久老妇app| 91老司机在线| 亚洲影院在线观看| 一级免费黄色录像| 久久久久久久国产精品视频| 老司机一区二区三区| 日本a在线免费观看| 国产成人福利夜色影视| 日本电影在线观看| 日本一区二区三区久久久久久久久不| 亚洲国产精品一区二区久久hs| 中文字幕一区二区人妻电影丶| 国产三级一区| 青青国产在线| 91精品电影| 污污的网站免费| 欧美伦理在线视频| 小日子的在线观看免费第8集| 无码av天堂一区二区三区| 老司机午夜激情| 91精品又粗又猛又爽| 亚洲午夜av久久乱码| 国产在线观看免费播放| 女人被狂躁c到高潮| 无码国产精品久久一区免费| 日韩av在线免费观看| 男人网站视频| 免费网站在线观看视频| 日本无删减在线| 久久久精品国产网站| 最近2019免费中文字幕视频三| 成人高潮视频| 国产欧美啪啪| 精品人妻一区二区三区四区不卡| 欧美激情网友自拍| 久久久精品午夜少妇| 日韩精品一区二区在线播放| 国产一区二区av在线| jizz性欧美2| av网站在线不卡| 亚洲国产精品久久久久秋霞不卡| 亚洲av综合色区无码另类小说| 成人免费视频国产免费观看| 精品国产18久久久久久二百| 亚洲最新永久在线观看| 国产亚洲欧美日韩在线观看一区二区| 国产精品久久久亚洲| 欧美女孩性生活视频| 日韩一区二区在线视频| 国产亚洲一区二区精品| 日韩美女在线| 国产日韩欧美在线一区| 国产调教一区二区三区| 五月婷婷久久综合| 久久五月天小说| 色婷婷激情一区二区三区| 成人台湾亚洲精品一区二区| 中文字幕一区二区三区四区久久| av网站在线免费播放| 在线成人免费观看| 亚洲熟女乱色一区二区三区| av电影在线观| 中文字幕一区综合| 日本欧美一二三区| 日韩大片免费在线观看| 欧美丰满美乳xxⅹ高潮www| 性网站在线免费观看| 蜜臀91精品一区二区三区| 一级黄色大片视频| 国产欧美在线一区二区| 欧美日韩xx| 久久综合亚洲精品| silk一区二区三区精品视频| 小水嫩精品福利视频导航| 1000部精品久久久久久久久| 国产日韩欧美在线观看视频| 日产精品99久久久久久| 91麻豆精品国产91久久久使用方法| 性欧美最新另类| 国产精品精品国产色婷婷| 免费毛片b在线观看| 天天躁日日躁狠狠躁av| 国产精品日韩精品欧美在线| 国产精品国产三级国产普通话对白| 中文字幕不卡在线观看| 俺来俺也去www色在线观看| 蜜桃精品wwwmitaows| 神马久久久久久| 中文字幕欧美在线| www日韩中文字幕在线看| 美女黄色片网站| 91蝌蚪porny成人天涯| 欧美欧美欧美欧美首页| 丁香激情综合国产| 日韩大胆成人| 97国产在线视频| 中文字幕av免费观看| 亚洲欧美小说色综合小说一区| 久久在线视频免费观看| 在线观看日韩精品视频| 国产亚洲成aⅴ人片在线观看| 欧美日韩不卡在线视频| 精品国产91乱码一区二区三区四区| 伊人222成人综合网| 日本妇女毛茸茸| 视频精品国内| 中文字幕日产av一二三区| 日本sm极度另类视频| 国产精品三上| 欧美成人做性视频在线播放| 最新日韩av在线| 2022国产麻豆剧果冻传媒剧情| 欧美另类视频在线观看| 性久久久久久久久久久久久久| 91成人精品网站| 欧美日韩国产免费| 亚洲国产一区二区三区在线播| 一区二区三区四区精品| 91麻豆精品91久久久久久清纯| 免费精品视频最新在线| 国产亚洲观看| 青青草原在线免费观看| 日韩精品免费一区| 亚洲pron| 精品视频网站| 成年人黄色大片在线| 亚洲欧美国产日韩中文字幕| 91丝袜在线| 久久精品欧美视频| 91深夜福利视频| 日韩黄色在线视频| 99久久久无码国产精品免费| 欧美va天堂| 亚洲精品国产九九九| 亚洲第一页在线播放| 日韩精品大片| 91a在线视频| 免费看成人人体视频| 久久久久久91| 久久er这里只有精品| 国产日产欧美精品一区二区三区| 男女视频免费看| 欧美日韩一区二区三区在线电影| 99久久久久成人国产免费| 裸体裸乳免费看| 亚洲综合图片区| 91黄色免费网站| 黄色欧美网站| 国产欧美一区二区三区网站| 亚洲欧美日韩中文在线制服| 精品91久久久| 一区二区在线不卡| 91大神在线播放精品| 久久综合99re88久久爱| 51国产成人精品午夜福中文下载| 成人xxxxx| 欧美女同在线观看| 天堂资源在线| 国产午夜精品一区在线观看| 中文幕一区二区三区久久蜜桃| 中文字幕永久在线视频| 男女视频免费网站| 欧美精品一级片| 精品一区二区三区欧美| 欧美猛男同性videos| 亚洲欧洲自拍| 一级二级三级在线观看| 国产精品色在线| 波多野结衣免费观看| 中文字幕免费在线播放| 国产精品国产三级国产普通话99| 欧美日韩激情视频一区二区三区| 国产福利一区二区三区视频| 婷婷丁香综合网| 欧美日韩黄网站| 欧美视频一区二区三区四区| 最近中文字幕在线mv视频在线| 中文字幕av资源| 极品美女一区二区三区视频| 中文字幕亚洲欧美日韩在线不卡| 欧美不卡一区二区三区四区| 亚洲国产成人一区二区| 久久伦理网站| 午夜精品一区二区三区视频免费看| 亚洲一区二区三区乱码aⅴ蜜桃女| 亚洲免费中文| 日韩欧美亚洲范冰冰与中字| 狠狠插狠狠操| 97自拍视频| 免费久久99精品国产| 国内在线免费高清视频| 六月天色婷婷| 日韩中文字幕区一区有砖一区| 国内精品久久久久国产盗摄免费观看完整版| 欧美性猛交xxxx免费看漫画| 一区二区在线免费观看| 欧美一区二区三区久久| 国产18精品乱码免费看| 欧美激情网站| 亚洲制服欧美久久| 日韩在线观看av| 成人黄色短视频在线观看| 中文字幕精品在线不卡| 男人网站在线观看| 欧美黑人一区二区三区| 久久久久久久久久久免费| 久久99热这里只有精品国产| 欧美三级资源在线| 国产浮力第一页| 国产精品久久久久福利| 久久欧美在线电影| 曰本三级在线| 精品一区二区三区久久久| 国产精品一区二区在线播放| 91精品国产福利尤物| 91精品国产全国免费观看| 精品久久久久久中文字幕| 在线看日本不卡| 午夜精品久久久久久久四虎美女版| 91美剧网在线播放| 久久裸体网站| 免费观看日韩毛片| 国产成a人亚洲精v品在线观看| 午夜精品一区二区三区免费视频| 丝袜在线观看| 日韩在线资源网| 国产网红主播福利一区二区| 黑人极品ⅴideos精品欧美棵| 成人污污www网站免费丝瓜| 蜜桃一区二区三区四区| www.色五月.com| 无码精品国产一区二区三区免费| 亚洲人成网站在线播放2019| 久久久亚洲欧洲日产国码αv| 欧美日韩在线电影| 在线能看的av网站| 久久亚洲中文字幕无码| 日韩成人综合网| 日韩欧美国产三级| 日韩美女一区二区三区在线观看| 久久伊伊香蕉| 91九色丨porny丨肉丝| 欧美日韩在线播放视频| 精品伊人久久久久7777人| 亚洲人成精品久久久| 一区二区三区在线高清| 日韩精品视频一区二区三区| 一个人看的www免费观看视频| 18被视频免费观看视频| 日韩欧美美女在线观看| 日韩成人影视| 高清视频一区二区| 国产精品直播网红| 国产成人天天5g影院在线观看| 一区二区三区在线视频看| 国产人妖在线观看| 狠狠躁夜夜躁人人爽超碰91| 亚洲男人的天堂在线视频| 日本一区二区三区高清不卡| 三上悠亚影音先锋| 精品国产乱码久久久久久果冻传媒| 99视频精品在线| 精品人妻av一区二区三区| 成人免费淫片95视频观看网站| 日韩电影在线观看完整版|