快速批量导入庞大数据到SQL SERVER数据库(ADO.NET)

原文地址:http://www.cnblogs.com/chenxizhang/archive/2008/11/11/1331060.html

如果你需要在程序中批量插入成千上万行的数据,你会怎么编写代码呢?最近在帮朋友调优这个的时候,总结了几种方法,并对其进行比较。

大概的界面如下,我模拟了一个客户资料表.

数据我是放在一个XML文件的,大约6734行。类似下面的格式

<?xml version="1.0" encoding="utf-8" ?>
<root>
<Customers>
  <CustomerID>ALFKI</CustomerID>
  <CompanyName>Sina</CompanyName>
  <ContactName>Maria Anders</ContactName>
  <ContactTitle>Sales Representative</ContactTitle>
  <Address>Obere Str. 57</Address>
  <City>Berlin</City>
  <PostalCode>12209</PostalCode>
  <Country>Germany</Country>
  <Phone>030-0074321</Phone>
  <Fax>030-0076545</Fax>
</Customers>
<Customers>
  <CustomerID>ANATR</CustomerID>
  <CompanyName>Ana Trujillo Emparedados y helados</CompanyName>
  <ContactName>Ana Trujillo</ContactName>
  <ContactTitle>Owner</ContactTitle>
  <Address>Avda. de la Constitución 2222</Address>
  <City>México D.F.</City>
  <PostalCode>05021</PostalCode>
  <Country>Mexico</Country>
  <Phone>(5) 555-4729</Phone>
  <Fax>(5) 555-3745</Fax>
</Customers>
<Customers>
  <CustomerID>ANTON</CustomerID>
  <CompanyName>Antonio Moreno Taquería</CompanyName>
  <ContactName>Antonio Moreno</ContactName>
  <ContactTitle>Owner</ContactTitle>
  <Address>Mataderos  2312</Address>
  <City>México D.F.</City>
  <PostalCode>05023</PostalCode>
  <Country>Mexico</Country>
  <Phone>(5) 555-3932</Phone>
</Customers>
</root>

下面首先在服务器稍微准备一下环境

USE [tempdb]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Customers](
    [CustomerID] [nchar](5) NOT NULL,
    [CompanyName] [nvarchar](40) NOT NULL,
    [ContactName] [nvarchar](30) NULL,
    [ContactTitle] [nvarchar](30) NULL,
    [Address] [nvarchar](60) NULL,
    [City] [nvarchar](15) NULL,
    [Region] [nvarchar](15) NULL,
    [PostalCode] [nvarchar](10) NULL,
    [Country] [nvarchar](15) NULL,
    [Phone] [nvarchar](24) NULL,
    [Fax] [nvarchar](24) NULL
) ON [PRIMARY]

CREATE PROCEDURE [dbo].[usp_InsertCustomer]
    @CustomerID nchar(5),
    @CompanyName nvarchar(40),
    @ContactName nvarchar(30),
    @ContactTitle nvarchar(30),
    @Address nvarchar(60),
    @City nvarchar(15),
    @Region nvarchar(15),
    @PostalCode nvarchar(10),
    @Country nvarchar(15),
    @Phone nvarchar(24),
    @Fax nvarchar(24)
AS

SET NOCOUNT ON

INSERT INTO [dbo].[Customers] (
    [CustomerID],
    [CompanyName],
    [ContactName],
    [ContactTitle],
    [Address],
    [City],
    [Region],
    [PostalCode],
    [Country],
    [Phone],
    [Fax]
) VALUES (
    @CustomerID,
    @CompanyName,
    @ContactName,
    @ContactTitle,
    @Address,
    @City,
    @Region,
    @PostalCode,
    @Country,
    @Phone,
    @Fax
)
 
 
我们在tempdb中创建了一个表和一个存储过程
 
首先,我们把数据加载到一个DataSet
        DataSet ds = new DataSet();
        private void btLoadData_Click(object sender, EventArgs e)
        {
            string dataFile = "CustomersData.xml";
            ds.ReadXml(dataFile);
            bindingSource1.DataSource = ds;
            bindingSource1.DataMember = "Customers";
            dataGridView1.DataSource = bindingSource1;

        }

然后,我们第一个测试代码是遍历这个DataSet,每一行提交一次

        private string GetConnectionString()
        {
            return "server=(local);database=tempdb;integrated security=true;";
        }
        /// <summary>
        /// 直接遍历,一个一个的提交给服务器。时间为265毫秒左右
        /// 每一行都需要写日志
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btOneByOne_Click(object sender, EventArgs e)
        {
            SqlConnection conn = new SqlConnection(GetConnectionString());
            SqlCommand cmd = conn.CreateCommand();
            cmd.CommandText = "usp_InsertCustomer";
            cmd.CommandType = CommandType.StoredProcedure;
            conn.Open();

            TimeSpan startTime = System.Diagnostics.Process.GetCurrentProcess().UserProcessorTime;

            foreach (DataRow row in ds.Tables[0].Rows)
            {
                cmd.Parameters.Clear();
                SqlParameter[] param = new SqlParameter[]{
                    new SqlParameter("@CustomerID",row[0].ToString()),
                    new SqlParameter("@CompanyName",row[1].ToString()),
                    new SqlParameter("@ContactName",row[2].ToString()),
                    new SqlParameter("@ContactTitle",row[3].ToString()),
                    new SqlParameter("@Address",row[4].ToString()),
                    new SqlParameter("@City",row[5].ToString()),
                    new SqlParameter("@Region",row[6].ToString()),
                    new SqlParameter("@PostalCode",row[7].ToString()),
                    new SqlParameter("@Country",row[8].ToString()),
                    new SqlParameter("@Phone",row[9].ToString()),
                    new SqlParameter("@Fax",row[10].ToString())
                };
                cmd.Parameters.AddRange(param);
                cmd.ExecuteNonQuery();
            }
            conn.Close();

            TimeSpan duration = System.Diagnostics.Process.GetCurrentProcess().UserProcessorTime.Subtract(startTime);
            MessageBox.Show("已经全部插入成功,所用时间为" + duration.Milliseconds.ToString() + "毫秒");
        }

接下来,我们使用ADO.NET内置的一个DataAdapter来提交

        /// <summary>
        /// 这是使用Adapter的方式,其实还是遍历,而且语法也没有简单
        /// 同时,速度甚至更慢。时间为650毫秒左右
        /// 每一行都需要写日志
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btUseAdapter_Click(object sender, EventArgs e)
        {
            SqlDataAdapter adapter = new SqlDataAdapter();
            adapter.AcceptChangesDuringUpdate = false;//为了演示目的,把这个开关关掉,以免它在更新完成后把数据集标记为未更改
            adapter.UpdateBatchSize = 10;//这个好像也没有什么用

            SqlConnection conn = new SqlConnection(GetConnectionString());
            SqlCommand insertCommand = conn.CreateCommand();
            insertCommand.CommandText = "usp_InsertCustomer";
            insertCommand.CommandType = CommandType.StoredProcedure;

            insertCommand.UpdatedRowSource = UpdateRowSource.None;

            insertCommand.Parameters.Add("@CustomerID", SqlDbType.NChar, 5, "CustomerID");
            insertCommand.Parameters.Add("@CompanyName", SqlDbType.NVarChar, 40, "CompanyName");
            insertCommand.Parameters.Add("@ContactName", SqlDbType.NVarChar, 30, "ContactName");
            insertCommand.Parameters.Add("@ContactTitle", SqlDbType.NVarChar, 30, "ContactTitle");
            insertCommand.Parameters.Add("@Address", SqlDbType.NVarChar, 60, "Address");
            insertCommand.Parameters.Add("@City", SqlDbType.NVarChar, 15, "City");
            insertCommand.Parameters.Add("@Region", SqlDbType.NVarChar, 15, "Region");
            insertCommand.Parameters.Add("@PostalCode", SqlDbType.NVarChar, 10, "PostalCode");
            insertCommand.Parameters.Add("@Country", SqlDbType.NVarChar, 15, "Country");
            insertCommand.Parameters.Add("@Phone", SqlDbType.NVarChar, 24, "Phone");
            insertCommand.Parameters.Add("@Fax", SqlDbType.NVarChar, 24, "Fax");

            adapter.InsertCommand = insertCommand;
            TimeSpan startTime = System.Diagnostics.Process.GetCurrentProcess().UserProcessorTime;
            adapter.Update(ds,"Customers");
            TimeSpan duration = System.Diagnostics.Process.GetCurrentProcess().UserProcessorTime.Subtract(startTime);
            MessageBox.Show("已经全部插入成功,所用时间为" + duration.Milliseconds.ToString() + "毫秒");

        }

最后,我们找到了最快的方法

        /// <summary>
        /// 使用新的API,批量导入,这个速度很快,大约26毫秒,很显然,这种方式只写一次日志,不会为每一行写日志
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btBCP_Click(object sender, EventArgs e)
        {
            using (SqlConnection conn = new SqlConnection(GetConnectionString()))
            {
                SqlBulkCopy bcp = new SqlBulkCopy(conn);
                bcp.DestinationTableName = "Customers";
                bcp.BatchSize = 100;//这是批尺寸可以调整
                for (int i = 0; i < 11; i++)
                {
                    bcp.ColumnMappings.Add(i, i);
                }
                TimeSpan startTime = System.Diagnostics.Process.GetCurrentProcess().UserProcessorTime;
                conn.Open();
                bcp.WriteToServer(ds.Tables[0]);
                TimeSpan duration = System.Diagnostics.Process.GetCurrentProcess().UserProcessorTime.Subtract(startTime);
                MessageBox.Show("已经全部插入成功,所用时间为" + duration.Milliseconds.ToString() + "毫秒");

            }
        }

还有一种办法是通过在服务器OPENXML,因为XML反复处理效率很差,所以就没有测试了,可以断定它肯定比其他几种还要慢。

另外提示一下,如果不用编程的方式,那么有其他三个可能的途径去做这个事情

1. BCP工具(这是一个命令行,可以做导入和导出,不过来源文件如果不规范,那么可能很费劲)

2. BULK INSERT语句(这是一个T-SQL语句,只能做导入,我们上面使用的SQLBULKCopy应该和他很类似)

3. XML Bulk Load(这是一套COM的对象模型,适合导入XML文档)

还有,在做大量的数据导入和导出时,可以考虑微软为SQL Server配套的SSIS(Integration Service)

时间: 2024-10-27 07:46:55

快速批量导入庞大数据到SQL SERVER数据库(ADO.NET)的相关文章

MVC3 Excel导入数据到sql Server数据库

MVC Excel导入到数据库,我们客户有一个固定的Excel,每个月都让我手动录入到库内(一开始不会SQL的导入)一两次我还好,蛮乐意的后来多了,我就使用了SQL自带的导入,可是每个月都这样,就太恶心了,为了凸显我是一个程序员,我跟项目经理提出,做一个页面上传Excel文件的页面,然后保存到数据库,让客户上传,天天上传都可以,不要每个月找我一次,而且客户还是个女的,最让我不开心的是她还那么土,然后项目经理说既然是你想出来的,那么你就自己写,我化石了,则个则.............ge.我是一

Excel表数据导入Sql Server数据库中

Excel表数据导入Sql Server数据库的方法很多,这里只是介绍了其中一种: 1.首先,我们要先在test数据库中新建一个my_test表,该表具有三个字段tid int类型, tname nvarchar类型, tt nvarchar类型(注意:my_test表中的数据类型必须与Excel中相应字段的类型一致) 2. 我们用SELECT * FROM  OPENROWSET( 'Microsoft.Jet.OLEDB.4.0 ', 'Excel  5.0;DatabASE=[Excel表

Excel导入数据到Sql server 中出错:“文本被截断,或者一个或多个字符在目标代码页中没有匹配项”

从Excel导入数据到Sql server 时,由于表中的数据有的很长,导入时出现如下错误(如果数据不是很长,255内以内,则不会出现错误): 出错原因: SQL Server的导入导出为了确定数据表的每个字段类型,取excel表的每个字段的前几行(3行即可)来判别.如果前3条是少于255的字符串,则设成nvarchar(255)类型,所以,当如果第四条或更后的数据的字符数大于255,则会报错. 解决方法: 在导入数据前,可以手动加几条无关的数据,让字段的字符数大于255,则导入时SQL Ser

转:SQL SERVER数据库中实现快速的数据提取和数据分页

探讨如何在有着1000万条数据的MS SQL SERVER数据库中实现快速的数据提取和数据分页.以下代码说明了我们实例中数据库的“红头文件”一表的部分数据结构: CREATE TABLE [dbo].[TGongwen] (    --TGongwen是红头文件表名 [Gid] [int] IDENTITY (1, 1) NOT NULL , --本表的id号,也是主键 [title] [varchar] (80) COLLATE Chinese_PRC_CI_AS NULL ,  --红头文件

极限挑战—C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码)

原文:极限挑战-C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码) 实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本实验将使用5中方法完成这个过程,并详细记录各种方法所耗费的时间. 本实验中所用到工具为VS2008和SQL SERVER 2000.SQL SERVER 2008,分别使用5中方法将100万条数据导入SQL 2000与SQL 2008中,实验环境是DELL 2850双2.0GCPU,2G内存的服务器.感兴趣的朋友可以下载源代码自己验证一下所用时间

从SQL Server数据库转到Oracle数据库的数据脚本处理

在我们很多情况下的开发,为了方便或者通用性的考虑,都首先考虑SQL Server数据库进行开发,但有时候客户的生产环境是Oracle或者其他数据库,那么我们就需要把对应的数据结构和数据脚本转换为对应的数据库,数据结构一般来说,语法都遵循了SQL92的标准,或者我们根据不同的PowerDesigner文件进行生成对应的结构脚本即可,但是实际数据的脚本我们就需要进行一定的处理,以及文本的替换处理了,本文结合Notepad++的文本正则表达式替换,实现一些如日期较为特殊的数据脚本调整,把它从SQL S

DataTable 导入 SQL SERVER 数据库

public void InsertTable(DataTable dt, string TabelName, DataColumnCollection dtColum) { //string str = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString.ToString(); //声明数据库连接 SqlConnection conn = new SqlConnection(strco

将txt文件内容导入sql server 数据库表中

将类似这样的txt文件,如何导入到sql server 数据库表中呢? 第一步,在开始菜单中 找到 sql server 的导入和导出数据 然后,在数据源 的下拉框中 选择 平面文件源 基本都是点击 下一步 选择好要导入数据的数据库 与表: 编辑映射 : 点击下一步 执行 然后 完成 查看数据库 表 数据已经导入 成功

漫谈可视化Prefuse(一)---从SQL Server数据库读取数据

上篇<可视化工具solo show-----Prefuse自带例子GraphView讲解>主要介绍了整个Prefuse工具集具有的一些特征.框架的运行流程,分析并展现了官方提供的例子GraphView.java. 这几天相继的看了prefuse.data.prefuse.data.expression等包的具体接口,大致了解了prefuse框架是如何完成外部数据与prefuse数据之间的映射关系转换:如何通过prefuse.data.expression包中的各个类完成对于逻辑表达式.字符串表