我必须从文本文件中插入大约200万行。
通过插入我必须创建一些主表。
将这么大的数据集插入SQL Server的最佳和最快捷的方法是什么?
答案 0 :(得分:59)
我认为您更好地阅读DataSet中的文本文件数据
试用 SqlBulkCopy - Bulk Insert into SQL from C# App
// connect to SQL
using (SqlConnection connection =
new SqlConnection(connString))
{
// make sure to enable triggers
// more on triggers in next post
SqlBulkCopy bulkCopy =
new SqlBulkCopy
(
connection,
SqlBulkCopyOptions.TableLock |
SqlBulkCopyOptions.FireTriggers |
SqlBulkCopyOptions.UseInternalTransaction,
null
);
// set the destination table name
bulkCopy.DestinationTableName = this.tableName;
connection.Open();
// write the data in the "dataTable"
bulkCopy.WriteToServer(dataTable);
connection.Close();
}
// reset
this.dataTable.Clear();
或
在顶部执行第1步后
您可以查看此文章了解详情:Bulk Insertion of Data Using C# DataTable and SQL server OpenXML function
但它没有经过200万条记录的测试,它会消耗内存,因为你必须加载200万条记录并插入它。
答案 1 :(得分:45)
答案 2 :(得分:19)
重新解决SqlBulkCopy的问题:
我使用StreamReader转换和处理文本文件。结果是我的对象列表。
我创建了一个类,而不是Datatable
或List<T>
和缓冲区大小(CommitBatchSize
)。它将使用扩展名(在第二个类中)将列表转换为数据表。
它的工作速度非常快。在我的电脑上,我可以在不到10秒的时间内插入超过1000万条复杂的记录。
这是班级:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DAL
{
public class BulkUploadToSql<T>
{
public IList<T> InternalStore { get; set; }
public string TableName { get; set; }
public int CommitBatchSize { get; set; }=1000;
public string ConnectionString { get; set; }
public void Commit()
{
if (InternalStore.Count>0)
{
DataTable dt;
int numberOfPages = (InternalStore.Count / CommitBatchSize) + (InternalStore.Count % CommitBatchSize == 0 ? 0 : 1);
for (int pageIndex = 0; pageIndex < numberOfPages; pageIndex++)
{
dt= InternalStore.Skip(pageIndex * CommitBatchSize).Take(CommitBatchSize).ToDataTable();
BulkInsert(dt);
}
}
}
public void BulkInsert(DataTable dt)
{
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
// make sure to enable triggers
// more on triggers in next post
SqlBulkCopy bulkCopy =
new SqlBulkCopy
(
connection,
SqlBulkCopyOptions.TableLock |
SqlBulkCopyOptions.FireTriggers |
SqlBulkCopyOptions.UseInternalTransaction,
null
);
// set the destination table name
bulkCopy.DestinationTableName = TableName;
connection.Open();
// write the data in the "dataTable"
bulkCopy.WriteToServer(dt);
connection.Close();
}
// reset
//this.dataTable.Clear();
}
}
public static class BulkUploadToSqlHelper
{
public static DataTable ToDataTable<T>(this IEnumerable<T> data)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
}
}
以下是我要插入自定义对象列表List<PuckDetection>
(ListDetections
)时的示例:
var objBulk = new BulkUploadToSql<PuckDetection>()
{
InternalStore = ListDetections,
TableName= "PuckDetections",
CommitBatchSize=1000,
ConnectionString="ENTER YOU CONNECTION STRING"
};
objBulk.Commit();
如果需要,可以修改BulkInsert
类以添加列映射。将Identity密钥作为第一列的示例。(假设数据表中的列名与数据库相同)
//ADD COLUMN MAPPING
foreach (DataColumn col in dt.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
答案 3 :(得分:4)
我使用bcp实用程序。 (批量复制程序) 我每个月加载大约150万条文本记录。 每个文本记录宽度为800个字符。 在我的服务器上,将150万条文本记录添加到SQL Server表中大约需要30秒。
bcp的说明位于http://msdn.microsoft.com/en-us/library/ms162802.aspx
答案 4 :(得分:3)
我最近遇到了这个场景(超过700万行),并且使用sqlcmd通过powershell(在将原始数据解析为SQL插入语句之后)一次性处理5,000个(SQL无法处理700万行)除非它被分解成更小的5K部分,否则一个块工作甚至500,000行。然后你可以一个接一个地运行每个5K脚本。)因为我需要利用SQL Server 2012 Enterprise中的新序列命令。我找不到一种编程方式,可以使用所述序列命令快速有效地插入700万行数据。
其次,在一次插入数百万行或更多数据时要注意的一件事是插入过程中的CPU和内存消耗(主要是内存)。如果没有释放所述进程,SQL将耗尽内存/ CPU的工作量。毋庸置疑,如果您的服务器上没有足够的处理能力或内存,您可以在短时间内轻松崩溃(我发现了很难)。如果你到达内存消耗超过70-75%的位置,只需重新启动服务器,进程就会恢复正常。
在实际制定最终执行计划之前,我必须运行一系列试错测试,看看我的服务器的限制是什么(考虑到有限的CPU /内存资源)。我建议你在测试环境中做同样的事情,然后再将其投入生产。
答案 5 :(得分:1)
我尝试了这种方法,它大大减少了我的数据库插入执行时间。
List<string> toinsert = new List<string>();
StringBuilder insertCmd = new StringBuilder("INSERT INTO tabblename (col1, col2, col3) VALUES ");
foreach (traverse your loop here)
{
toinsert.Add(string.Format("( '{0}', '{1}', '{2}' )", "Val1", "Val2", "Val3"));
}
if (toinsert.Count != 0)
{
insertCmd.Append(string.Join(",", toinsert));
insertCmd.Append(";");
}
using (MySqlCommand myCmd = new MySqlCommand(insertCmd.ToString(), SQLconnectionObject))
{
myCmd.CommandType = CommandType.Text;
myCmd.ExecuteNonQuery();
}
*创建SQL连接对象,并在我编写SQLconnectionObject的地方替换它。
答案 6 :(得分:0)
我遇到了一个应该与 ADO、Entity 和 Dapper 一起使用的解决方案问题,因此制作了 this lib;它以以下形式生成批次:
IEnumerable<(string SqlQuery, IEnumerable<SqlParameter> SqlParameters)>
IEnumerable<(string SqlQuery, DynamicParameters DapperDynamicParameters)>
this link 包含说明。对 SQL 注入是安全的,因为使用参数而不是串联;如果需要,您也可以通过可选参数将身份插入设置为 ON。
与 ADO.NET 一起使用:
using MsSqlHelpers;
// ...
var mapper = new MapperBuilder<Person>()
.SetTableName("People")
.AddMapping(person => person.FirstName, columnName: "Name")
.AddMapping(person => person.LastName, columnName: "Surename")
.AddMapping(person => person.DateOfBirth, columnName: "Birthday")
.Build();
var people = new List<Person>()
{
new Person()
{
FirstName = "John",
LastName = "Lennon",
DateOfBirth = new DateTime(1940, 10, 9)
},
new Person()
{
FirstName = "Paul",
LastName = "McCartney",
DateOfBirth = new DateTime(1942, 6, 18)
},
};
var connectionString = "Server=SERVER_ADDRESS;Database=DATABASE_NAME;User Id=USERNAME;Password=PASSWORD;";
var sqlQueriesAndParameters = new MsSqlQueryGenerator()
.GenerateParametrizedBulkInserts(mapper, people);
using (var sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
// Default batch size: 1000 rows or (2100-1) parameters per insert.
foreach (var (SqlQuery, SqlParameters) in sqlQueriesAndParameters)
{
using (SqlCommand sqlCommand = new SqlCommand(SqlQuery, sqlConnection))
{
sqlCommand.Parameters.AddRange(SqlParameters.ToArray());
sqlCommand.ExecuteNonQuery();
}
}
}
与 Dapper 一起使用:
using MsSqlHelpers;
// ...
var mapper = new MapperBuilder<Person>()
.SetTableName("People")
.AddMapping(person => person.FirstName, columnName: "Name")
.AddMapping(person => person.LastName, columnName: "Surename")
.AddMapping(person => person.DateOfBirth, columnName: "Birthday")
.Build();
var people = new List<Person>()
{
new Person()
{
FirstName = "John",
LastName = "Lennon",
DateOfBirth = new DateTime(1940, 10, 9)
},
new Person()
{
FirstName = "Paul",
LastName = "McCartney",
DateOfBirth = new DateTime(1942, 6, 18)
},
};
var connectionString = "Server=SERVER_ADDRESS;Database=DATABASE_NAME;User Id=USERNAME;Password=PASSWORD;";
var sqlQueriesAndDapperParameters = new MsSqlQueryGenerator()
.GenerateDapperParametrizedBulkInserts(mapper, people);
using (var sqlConnection = new SqlConnection(connectionString))
{
// Default batch size: 1000 rows or (2100-1) parameters per insert.
foreach (var (SqlQuery, DapperDynamicParameters) in sqlQueriesAndDapperParameters)
{
sqlConnection.Execute(SqlQuery, DapperDynamicParameters);
}
}
与实体框架一起使用:
using MsSqlHelpers;
// ...
var mapper = new MapperBuilder<Person>()
.SetTableName("People")
.AddMapping(person => person.FirstName, columnName: "Name")
.AddMapping(person => person.LastName, columnName: "Surename")
.AddMapping(person => person.DateOfBirth, columnName: "Birthday")
.Build();
var people = new List<Person>()
{
new Person()
{
FirstName = "John",
LastName = "Lennon",
DateOfBirth = new DateTime(1940, 10, 9)
},
new Person()
{
FirstName = "Paul",
LastName = "McCartney",
DateOfBirth = new DateTime(1942, 6, 18)
},
};
var sqlQueriesAndParameters = new MsSqlQueryGenerator()
.GenerateParametrizedBulkInserts(mapper, people);
// Default batch size: 1000 rows or (2100-1) parameters per insert.
foreach (var (SqlQuery, SqlParameters) in sqlQueriesAndParameters)
{
_context.Database.ExecuteSqlRaw(SqlQuery, SqlParameters);
// Depracated but still works: _context.Database.ExecuteSqlCommand(SqlQuery, SqlParameters);
}