Sql Server海量数据插入
阅读原文时间:2024年06月28日阅读:1

目录

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文件中,关键代码如下:  

///

/// 生成测试数据 ///
private static void GenerateTestData()
{
string fileName = "sql";

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

  第三步:封装出两个方法,分别用来执行批量插入和普通插入,具体代码如下:

///

/// 批量插入测试 ///
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 = ;  
        while (i <= )  
        {  
            string sqlInsert = string.Format("insert into Student1(id,Name,Age) values({0},'test{0}',{0})", i);  
            new DBHelper().Excute(sqlInsert);  
            i++;  
        }  
    }

  第四步: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 = ;  
        while (i <= )  
        {  
            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 = ;  
        while (i <= )  
        {  
            string strInsert = string.Format("{0},'test{0}',{0}|", i);  
            File.AppendText(strInsert, fileName);  
            i++;  
        }  
    }  
}  

}

  示例中还用到两个辅助类,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 = ;  
        command.Connection = conn;  
        command.CommandText = sql;  
        conn.Open();  
        command.ExecuteNonQuery();  
        conn.Close();  
    }  
}  

}

   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);  
        }  
    }  
}  

}

  一切准备就绪,开始运行,结果如下:

  

  其中单位为毫秒,从结果我们可以看出BULK INSER插入500000条数据还不需要3秒,而普通逐条插入却需要20多分钟