C# 如何在尽可能短的时间内插入1000万条记录?
我有一个文件(有1000万条记录),如下所示:C# 如何在尽可能短的时间内插入1000万条记录?,c#,sql-server,import,bulkinsert,table-valued-parameters,C#,Sql Server,Import,Bulkinsert,Table Valued Parameters,我有一个文件(有1000万条记录),如下所示: line1 第2行 第3行 第4行 ....... ...... 1000万行 所以基本上我想在数据库中插入1000万条记录。 因此,我读取了该文件并将其上载到SQL Server C#代码 System.IO.StreamReader文件= 新System.IO.StreamReader(@“c:\test.txt”); 而((line=file.ReadLine())!=null) { //插入代码在这里 //DAL.ExecuteSql(“
line1
第2行
第3行
第4行
.......
......
1000万行
所以基本上我想在数据库中插入1000万条记录。
因此,我读取了该文件并将其上载到SQL Server
C#代码
System.IO.StreamReader文件=
新System.IO.StreamReader(@“c:\test.txt”);
而((line=file.ReadLine())!=null)
{
//插入代码在这里
//DAL.ExecuteSql(“插入表1中的值(“+line+”));
}
file.Close();
但插入需要很长时间。
如何使用C#在尽可能短的时间内插入1000万条记录
更新1:批量插入:
批量插入DBNAME.dbo.DATAs
来自“F:\dt10000000\dt10000000.txt”
具有
(
行终止符='\n'
);
我的桌子如下:
数据
(
数据字段VARCHAR(最大值)
)
但我得到了以下错误:
Msg 4866,第16级,状态1,第1行批量加载失败。数据文件中第1行第1列的列太长。验证是否正确指定了字段终止符和行终止符 味精7399,第16级,状态1,第1行
链接服务器(null)的OLE DB提供程序“大容量”报告了错误。提供程序没有提供有关错误的任何信息 信息7330,16级,状态2,第1行
无法从链接服务器(null)的OLE DB提供程序“大容量”中获取行 以下代码有效:
批量插入DBNAME.dbo.DATAs
来自“F:\dt10000000\dt10000000.txt”
具有
(
字段终止符='\t',
行终止符='\n'
);
最好的方法是将第一种解决方案和第二种解决方案混合使用,
创建DataTable
并在循环中向其添加行,然后使用BulkCopy
上载
在一个连接中连接到DB
另外需要注意的是,大容量复制是一项非常敏感的操作,几乎
每个错误都会使副本无效,例如,如果您在dataTable中声明列名为“text”,在DB中声明列名为“text”,则会引发异常,祝您好运。请不要创建要通过BulkCopy加载的dataTable
。对于较小的数据集来说,这是一个不错的解决方案,但是在调用数据库之前,绝对没有理由将所有1000万行加载到内存中
您的最佳选择(在BCP
/批量插入
/OPENROWSET(批量…
)之外)是通过表值参数(TVP)将文件中的内容流式传输到数据库中。通过使用TVP,您可以打开文件,读取一行并发送一行,直到完成,然后关闭文件。此方法的内存占用仅为一行。我写了一篇文章,其中有一个例子就是这种情况
结构的简单概述如下。我假设导入表和字段名与上述问题中显示的相同
所需的数据库对象:
--首先:您需要一个用户定义的表类型
创建类型ImportStructure作为表(字段VARCHAR(MAX));
去
--第二:将UDTT用作导入过程的输入参数。
--因此,“表值参数”(TVP)
创建过程dbo.ImportData(
@可导入dbo.ImportStructure只读
)
作为
不计数;
--也许先把桌子清理干净?
截断表dbo.DATAs;
插入dbo.DATAs(数据字段)
选择字段
来自@importable;
去
下面是使用上述SQL对象的C#应用程序代码。请注意,在这种方法中,不是填充对象(例如DataTable)然后执行存储过程,而是执行存储过程来启动文件内容的读取。存储过程的输入参数不是变量;它是方法的返回值,GetFileContents
。当SqlCommand
调用ExecuteNonQuery
,打开文件,读取一行,并通过IEnumerable
和yield return
构造将行发送到SQL Server,然后关闭文件时,将调用该方法。存储过程只看到一个表变量@importable,一旦数据开始出现,就可以访问它(注意:数据在tempdb中会持续很短时间,即使不是完整的内容)
使用系统集合;
使用系统数据;
使用System.Data.SqlClient;
使用System.IO;
使用Microsoft.SqlServer.Server;
私有静态IEnumerable GetFileContents()
{
SqlMetaData[]\u TvpSchema=新的SqlMetaData[]{
新的SqlMetaData(“字段”,SqlDbType.VarChar,SqlMetaData.Max)
};
SqlDataRecord _DataRecord=新的SqlDataRecord(_TvpSchema);
StreamReader _FileReader=null;
尝试
{
_FileReader=新的StreamReader(“{filePath}”);
//读一行,发一行
而(!\u FileReader.EndOfStream)
{
//您不需要将“\u DataRecord=new SqlDataRecord”作为
//SQL Server在调用“yield return”时已收到该行。
//与BCP和大容量插入不同,您可以在此处选择创建字符串
//将ReadLine()调用到字符串中,对
//字符串,然后将该字符串传递到SetString()中,如果无效,则放弃该字符串。
_DataRecord.SetString(0,_FileReader.ReadLine());
收益率返回数据记录;
}
}
最后
{
_FileReader.Close();
}
}
上面的GetFileContents
方法用作存储过程的输入参数值,如下所示:
公共静态无效测试()
{
SqlConnection _Connection=newsqlconnection(“{Connection string}”);
SqlCommand _Command=newsqlcommand(“ImportData”,_连接);
_Command.CommandType=CommandType.storedProcess;
SqlParameter _TVParam=新的SqlParameter();
_TVParam.ParameterName=“@importable”;
_TVParam.TypeName=“dbo.ImportStructure”;
_TVParam.SqlDbType=SqlDbType.St
CREATE TABLE TestData(ID INT IDENTITY (1,1), CreatedDate DATETIME)
GO
INSERT INTO TestData(CreatedDate) SELECT GetDate()
GO 10000000