在多种不同的场景中,经常需要进行数据的迁移、备份或从不同的数据源收集数据。例如,在将旧系统迁移到新系统的过程中,或者在需要备份数据以防止数据丢失时,都需要进行数据的批量复制。.NET Framework 2.0 提供了一个名为 SqlBulkCopy
的类,它可以帮助将数据从不同的数据源复制到 SQL SERVER 数据库中。本文将展示 SqlBulkCopy
类的不同使用方面。
数据库设计相对简单,它基于 Northwind 数据库中的 Products
表。在 Northwind 数据库中创建了三个额外的表。查看下面的数据库图示,以获得更好的理解。
Products_Archive
和 Products_Latest
表具有与 Products
表相同的模式,而 Products_TopSelling
表则不同。将在本文稍后解释 Products_TopSelling
表的用途。
Products_Archive
表包含 770,000 行数据。不需要担心这些行是如何产生的;只需要考虑如何将所有这些行移动到 Products_Latest
表中。
SqlBulkCopy
类包含一个实例方法 WriteToServer
,它用于将数据从源传输到目标。WriteToServer
方法可以执行 DataRow[]
数组、DataTable
和 DataReader
的操作。根据情况,可以选择喜欢的容器,但在大多数情况下,选择 DataReader
是一个好主意。这是因为 DataReader
是一个只向前、只读的流。它不持有数据,因此比 DataTable
和 DataRows[]
快得多。下面的代码用于将数据从源表传输到目标表。
private static void PerformBulkCopy()
{
string connectionString = @"
Server=localhost;Database=Northwind;Trusted_Connection=true";
// 获取源数据
using (SqlConnection sourceConnection = new SqlConnection(connectionString))
{
SqlCommand myCommand = new SqlCommand("SELECT * FROM Products_Archive", sourceConnection);
sourceConnection.Open();
SqlDataReader reader = myCommand.ExecuteReader();
// 打开目标数据
using (SqlConnection destinationConnection = new SqlConnection(connectionString))
{
// 打开连接
destinationConnection.Open();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationConnection.ConnectionString))
{
bulkCopy.BatchSize = 500;
bulkCopy.NotifyAfter = 1000;
bulkCopy.SqlRowsCopied += new SqlRowsCopiedEventHandler(bulkCopy_SqlRowsCopied);
bulkCopy.DestinationTableName = "Products_Latest";
bulkCopy.WriteToServer(reader);
}
}
reader.Close();
}
}
这里有几个要点需要提及。首先,使用 DataReader
从数据库表中获取行。SqlBulkCopy
类对象 "bulkCopy" 设置了 DestinationTableName
属性为目标表,在这个例子中是 "Products_Latest"。Products_Latest
是目标表,因为数据是从 Products_Archive
表传输到 Products_Latest
表。bulkCopy
对象还公开了 SqlRowsCopied
事件,该事件在达到 NotifyAfter
属性标识的行数后触发。这意味着事件将在每 1000 行之后触发,因为 NotifyAfter
设置为 1000。
BatchSize
属性非常重要,因为大部分性能都依赖于它。BatchSize
意味着一次将多少行发送到数据库以启动数据传输。将 BatchSize
设置为 500,这意味着一旦读取器读取了 500 行,它们将被发送到数据库以执行批量复制操作。默认情况下,BatchSize
是 "1",这意味着每一行都作为单个批次发送到数据库。不同的 BatchSize
会给不同的结果。应该测试哪个批次大小适合需求。
在上面的例子中,两个表具有相同的模式。有时,需要在模式不同的表之间传输数据。假设想将 Products_Archive
表中的所有产品名称和数量传输到 Products_TopSelling
表中。这两个表的模式是不同的,因为它们具有不同的列名。这在上面的数据库设计部分的图像中也可以看到。
private static void PerformBulkCopyDifferentSchema()
{
string connectionString = @"
Server=localhost;Database=Northwind;Trusted_Connection=true";
DataTable sourceData = new DataTable();
// 获取源数据
using (SqlConnection sourceConnection = new SqlConnection(connectionString))
{
SqlCommand myCommand = new SqlCommand("SELECT TOP 5 * FROM Products_Archive", sourceConnection);
sourceConnection.Open();
SqlDataReader reader = myCommand.ExecuteReader();
// 打开目标数据
using (SqlConnection destinationConnection = new SqlConnection(connectionString))
{
// 打开连接
destinationConnection.Open();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationConnection.ConnectionString))
{
bulkCopy.ColumnMappings.Add("ProductID", "ProductID");
bulkCopy.ColumnMappings.Add("ProductName", "Name");
bulkCopy.ColumnMappings.Add("QuantityPerUnit", "Quantity");
bulkCopy.DestinationTableName = "Products_TopSelling";
bulkCopy.WriteToServer(reader);
}
}
reader.Close();
}
}
ColumnMappings
集合用于映射源表和目标表之间的列。
数据源不仅限于数据库表,还可以使用 XML 文件。这里有一个非常简单的 XML 文件,用作批量复制操作的源。(Products.xml)
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product productID="1" productName="Chai"/>
<Product productID="2" productName="Football"/>
<Product productID="3" productName="Soap"/>
<Product productID="4" productName="Green Tea"/>
</Products>
private static void PerformBulkCopyXMLDataSource()
{
string connectionString = @"
Server=localhost;Database=Northwind;Trusted_Connection=true";
DataSet ds = new DataSet();
DataTable sourceData = new DataTable();
ds.ReadXml(@"C:\Products.xml");
sourceData = ds.Tables[0];
// 打开目标数据
using (SqlConnection destinationConnection = new SqlConnection(connectionString))
{
// 打开连接
destinationConnection.Open();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationConnection.ConnectionString))
{
// 列映射
bulkCopy.ColumnMappings.Add("productID", "ProductID");
bulkCopy.ColumnMappings.Add("productName", "Name");
bulkCopy.DestinationTableName = "Products_TopSelling";
bulkCopy.WriteToServer(sourceData);
}
}
}
文件首先被读取到 DataTable
中,然后被传递给 SqlBulkCopy
类的 WriteToServer
方法。由于目标表是 Products_TopSelling
,不得不执行列映射。