Sql Server海量數據插入


目錄

1.前言

2.BULK INSERT

3.簡單示例

 

前言

  由於昨天接到一個客戶反饋導出數據卡死的問題,於是決定今天模擬一下千萬級的數據,然后傻傻的等待插入數據了半天...... 

  對於海量數據,上百萬上千萬的數據插入,我們用ADO.NET提供的普通一條一條數據插入非常非常慢,好在Sql Server為我們提供了批量插入方法。

 

BULK INSERT

  語法

  

  主要參數說明

  database_name

  指定的表或視圖所在的數據庫的名稱,如果未指定,則默認為當前數據庫。

  schema_name

  表或視圖架構的名稱。

  table_name

  要將數據大容量導入其中的表或視圖的名稱。

  ‘data_file’

  數據文件的完整路徑,該數據文件包含到導入到指定表或視圖中的數據。使用BULK INSERT可以從磁盤導入數據。

  BATCHSIZE=batch_size

  指定批量處理中的行數。每個批處理作為一個事物復制到服務器。

  CHECK_CONSTRAINTS
      指定在大容量導入操作期間,必須檢查所有對目標表或視圖的約束。

  FIELDTERMINATOR ='field_terminator'
  指定要用於 char 和 widechar 數據文件的字段終止符,即字段的分隔符。 默認的字段終止符是 \t(制表符)。

  ROWTERMINATOR ='row_terminator'
  指定要用於 char 和 widechar 數據文件的行終止符,即行的分隔符。

  

  更多參數說明,請參考: https://msdn.microsoft.com/zh-cn/library/ms188365.aspx

 

簡單示例

  為了對比BULK INSERT和普通逐條插入的差異,我們通過一個簡單的示例,通過實際運行來查看效果。  

  第一步:在數據庫新建兩張一樣的表,分表為Student和Student1,表結構完全相同,只有ID,NAME,AGE三個簡單的字段。

  

  第二步:新建一個控制台程序,通過一個簡單的循環,生成500000條數據寫入到txt文件中,關鍵代碼如下:  

 /// <summary>
        /// 生成測試數據
        /// </summary>
        private static void GenerateTestData()
        {
            string fileName = "sql";

            int i = 1;
            while (i <= 500000)
            {
                string strInsert = string.Format("{0},'test{0}',{0}|", i);
                File.AppendText(strInsert, fileName);
                i++;
            }
        }
View Code

 

  第三步:封裝出兩個方法,分別用來執行批量插入和普通插入,具體代碼如下:

/// <summary>
        /// 批量插入測試
        /// </summary>
        private static void BulkInsertTest()
        {
            string strFilePath = @"D:\學習\ASP.NET\QYH.BlukInsertTest\sql.txt";
            string strTableName = "Student";

            /* 每一個字段的信息以“,”分割 
            *每一條數據以“|”符號分隔
            * 每10萬條數據一個事務*/
            string sql = string.Format("BULK INSERT {0} FROM '{1}' WITH (FIELDTERMINATOR = ',',ROWTERMINATOR ='|',BATCHSIZE = 50000)", strTableName, strFilePath);
            DBHelper dbHelper = new DBHelper();
            dbHelper.Excute(sql);

        }

        /// <summary>
        /// 普通插入測試
        /// </summary>
        private static void CommonInsertTest()
        {
            int i = 1;
            while (i <= 500000)
            {
                string sqlInsert = string.Format("insert into Student1(id,Name,Age) values({0},'test{0}',{0})", i);
                new DBHelper().Excute(sqlInsert);
                i++;
            }
        }
View Code

 

   第四步:Main主函數中調用批量插入和普通插入方法,並通過Stopwatch計算出執行時間,Pragram完整代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using QYH.BlukInsertTest.FileMange;
using QYH.BlukInsertTest.DataBase;
using System.Diagnostics;

namespace QYH.BlukInsertTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //用於生成海量數據
            //GenerateTestData();

            Stopwatch stopwatch = Stopwatch.StartNew();
            try
            {
                BulkInsertTest();
            }
            catch (Exception)
            {

                //throw;
            }

            stopwatch.Stop();
            string strResult = "批量插入耗時:" + stopwatch.ElapsedMilliseconds.ToString();

            Stopwatch stopwatch1 = Stopwatch.StartNew();
            CommonInsertTest();
            stopwatch1.Stop();
            string str1Result = "普通插入耗時:" + stopwatch1.ElapsedMilliseconds.ToString();

            string strTestResult = "result";
            File.WriteTextAsync(strResult + "\r\n" + str1Result, strTestResult);

            //Console.Read();
        }

        /// <summary>
        /// 批量插入測試
        /// </summary>
        private static void BulkInsertTest()
        {
            string strFilePath = @"D:\學習\ASP.NET\QYH.BlukInsertTest\sql.txt";
            string strTableName = "Student";

            /* 每一個字段的信息以“,”分割 
            *每一條數據以“|”符號分隔
            * 每10萬條數據一個事務*/
            string sql = string.Format("BULK INSERT {0} FROM '{1}' WITH (FIELDTERMINATOR = ',',ROWTERMINATOR ='|',BATCHSIZE = 50000)", strTableName, strFilePath);
            DBHelper dbHelper = new DBHelper();
            dbHelper.Excute(sql);

        }

        /// <summary>
        /// 普通插入測試
        /// </summary>
        private static void CommonInsertTest()
        {
            int i = 1;
            while (i <= 500000)
            {
                string sqlInsert = string.Format("insert into Student1(id,Name,Age) values({0},'test{0}',{0})", i);
                new DBHelper().Excute(sqlInsert);
                i++;
            }
        }

        /// <summary>
        /// 生成測試數據
        /// </summary>
        private static void GenerateTestData()
        {
            string fileName = "sql";

            int i = 1;
            while (i <= 500000)
            {
                string strInsert = string.Format("{0},'test{0}',{0}|", i);
                File.AppendText(strInsert, fileName);
                i++;
            }
        }
    }
}
View Code

  示例中還用到兩個輔助類,DBHelper.cs和File.cs,由於僅用於演示,所以寫的非常簡單,其中文件路徑是寫死的,可以替換成實際路徑。

  DBHelper.cs  

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QYH.BlukInsertTest.DataBase
{
    public class DBHelper
    {
        public string connectionString = "Server=.;Database=QYHDB;User ID=sa;Password=123456;Trusted_Connection=False;";

        public void Excute(string sql)
        {
            SqlConnection conn = new SqlConnection(connectionString);
            SqlCommand command = new SqlCommand();
            command.CommandTimeout = 0;
            command.Connection = conn;
            command.CommandText = sql;
            conn.Open();
            command.ExecuteNonQuery();
            conn.Close();
        }
    }
}
View Code

 

   File.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QYH.BlukInsertTest.FileMange
{
    public class File
    {
        public static string strFilePath = @"D:\學習\ASP.NET\QYH.BlukInsertTest";

        public static async void WriteTextAsync(string text, string fileName)
        {
            using (StreamWriter outputFile = new StreamWriter(strFilePath + @"\" + fileName + ".txt"))
            {
                await outputFile.WriteAsync(text);
            }
        }

        public static void AppendText(string text, string fileName)
        {
            // Append text to an existing file named "WriteLines.txt".
            using (StreamWriter outputFile = new StreamWriter(strFilePath + @"\" + fileName + ".txt",true))
            {
                outputFile.WriteLine(text);
            }
        }
    }
}
View Code

 

  

  一切准備就緒,開始運行,結果如下:

  

  其中單位為毫秒,從結果我們可以看出BULK INSER插入500000條數據還不需要3秒,而普通逐條插入卻需要20多分鍾

 

      

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM