SQL Bulk Insert 快速插入

Posted twelvezuo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL Bulk Insert 快速插入相关的知识,希望对你有一定的参考价值。

SQL INSERT INTO 语句

INSERT INTO 语句用于向表中插入新记录。

SQL INSERT INTO 语法

INSERT INTO 语句可以有两种编写形式。

① 第一种形式无需指定要插入数据的列名,只需提供被插入的值即可:

INSERT INTO table_name
VALUES (value1,value2,value3,...);

② 第二种形式需要指定列名及被插入的值:

INSERT INTO table_name (column1,column2,column3,...)
VALUES (value1,value2,value3,...);

BULK INSERT 语句

BULK INSERT 用于海量数据插入

BULK INSERT 语法

BULK INSERT 
[ database_name . [ schema_name ] . | schema_name . ] [ table_name | view_name ]
FROM ‘data_file‘
[ WITH
(
[ [ , ] BATCHSIZE = batch_size ] --BATCHSIZE指令来设置在单个事务中可以插入到表中的记录的数量
[ [ , ] CHECK_CONSTRAINTS ] --指定在大容量导入操作期间,必须检查所有对目标表或视图的约束。若没有 CHECK_CONSTRAINTS 选项,则所有 CHECK 和 FOREIGN KEY 约束都将被忽略,并且在此操作之后表的约束将标记为不可信。
[ [ , ] CODEPAGE = { ‘ACP‘ | ‘OEM‘ | ‘RAW‘ | ‘code_page‘ } ] --指定该数据文件中数据的代码页
[ [ , ] DATAFILETYPE =
{ ‘char‘ | ‘native‘| ‘widechar‘ | ‘widenative‘ } ] --指定 BULK INSERT 使用指定的数据文件类型值执行导入操作。
[ [ , ] FIELDTERMINATOR = ‘field_terminator‘ ] --标识分隔内容的符号
[ [ , ] FIRSTROW = first_row ] --指定要加载的第一行的行号。默认值是指定数据文件中的第一行
[ [ , ] FIRE_TRIGGERS ] --是否启动触发器
[ [ , ] FORMATFILE = ‘format_file_path‘ ]
[ [ , ] KEEPIDENTITY ] --指定导入数据文件中的标识值用于标识列
[ [ , ] KEEPNULLS ] --指定在大容量导入操作期间空列应保留一个空值,而不插入用于列的任何默认值
[ [ , ] KILOBYTES_PER_BATCH = kilobytes_per_batch ]
[ [ , ] LASTROW = last_row ] --指定要加载的最后一行的行号
[ [ , ] MAXERRORS = max_errors ] --指定允许在数据中出现的最多语法错误数,超过该数量后将取消大容量导入操作。
[ [ , ] ORDER ( { column [ ASC | DESC ] } [ ,...n ] ) ] --指定数据文件中的数据如何排序
[ [ , ] ROWS_PER_BATCH = rows_per_batch ]
[ [ , ] ROWTERMINATOR = ‘row_terminator‘ ] --标识分隔行的符号
[ [ , ] TABLOCK ] --指定为大容量导入操作持续时间获取一个表级锁
[ [ , ] ERRORFILE = ‘file_name‘ ] --指定用于收集格式有误且不能转换为 OLE DB 行集的行的文件。
)]

简单示例

为了对比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++;
}
}

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

 /// <summary>
/// 批量插入测试
/// </summary>
private static void BulkInsertTest()
{
string strFilePath = @"D:学习ASP.NETQYH.BlukInsertTestsql.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++;
}
}

第四步: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 + " " + str1Result, strTestResult);

//Console.Read();
}

/// <summary>
/// 批量插入测试
/// </summary>
private static void BulkInsertTest()
{
string strFilePath = @"D:学习ASP.NETQYH.BlukInsertTestsql.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++;
}
}
}
}

示例中还用到两个辅助类,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();
}
}
}

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.NETQYH.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多分钟

以上是关于SQL Bulk Insert 快速插入的主要内容,如果未能解决你的问题,请参考以下文章

SQL SERVER 使用BULK Insert将txt文件中的数据批量插入表中

获取 Bulk.Insert() -Mongoskin 的插入 ID

获取 Bulk.Insert() -Mongoskin 的插入 ID

bulk.insert(doc) 默认值无法插入 MongoDB nodejs

bulk.insert(doc) 默认值无法插入 MongoDB nodejs

如何在 SQL Standard 上导出 SSIS 数据并在 SQL Express 上使用 Bulk Insert 导入?