列表到没有数据表的SqlBulkCopy

时间:2018-01-12 11:23:59

标签: c# .net multithreading sqlbulkcopy

我们有一个大的列表,大约有100000条记录,并希望将其插入到sql表中。

我们在做什么;将该列表转换为数据表并将datatable传递给SqlBulkcopy方法。

从列表转换为Datatable需要更多时间。尝试使用Parallel但作为Datatable不是线程安全的,所以避免这样做。

添加生成整数列表的样本poc代码并将其插入临时表

static void Main(string[] args)
    {
        List<int> valueList = GenerateList(100000);

        Console.WriteLine("Starting with Bulk Insert ");

         DateTime startTime = DateTime.Now;
        int recordCount = BulkInsert(valueList);

        TimeSpan ts = DateTime.Now.Subtract(startTime);

        Console.WriteLine("Bulk insert for {0} records in {1} miliseconds.-> ", recordCount, ts.Milliseconds);

        Console.WriteLine("Done.");
        Console.ReadLine();

    }

    private static int BulkInsert(List<int> valueList)
    {
        SqlBulkHelper sqlBulkHelper = new SqlBulkHelper();

        var eventIdDataTable = CreateIdentityDataTable(valueList, "SqlTable", "Id");
        return FillBulkPoundTable(eventIdDataTable, "#SqlTable");
    }

    private static List<int> GenerateList(int size)
    {
        return Enumerable.Range(0, size).ToList();
    }

    private static DataTable CreateIdentityDataTable(List<int> ids, string dataTableName, string propertyName)
    {
        if (ids == null) return null;

        using (var dataTable = new DataTable(dataTableName))
        {
            dataTable.Locale = CultureInfo.CurrentCulture;
            var dtColumn = new DataColumn(propertyName, Type.GetType("System.Int32"));
            dataTable.Columns.Add(dtColumn);

            foreach (int id in ids)
            {
                DataRow row = dataTable.NewRow();
                row[propertyName] = id;
                dataTable.Rows.Add(row);
            }
            return dataTable;
        }
    }

    private static int FillBulkPoundTable(DataTable dataTable, string destinationTableName)
    {
        int totalInsertedRecordCount = 0;
        using (SqlConnection _connection = new SqlConnection(CongifUtil.sqlConnString))
        {
            string sql =
                 @"If object_Id('tempdb..#EventIds') is not null drop table #EventIds
                  CREATE TABLE #EventIds(EvId int) ";


            _connection.Open();
            using (var command = new SqlCommand(sql, _connection))
            {
                command.ExecuteNonQuery();
            }

            using (var sqlBulkCopy = new SqlBulkCopy(_connection))
            {
                sqlBulkCopy.BulkCopyTimeout = 0;
                sqlBulkCopy.DestinationTableName = destinationTableName;
                sqlBulkCopy.WriteToServer(dataTable);
            }

            using (var command = new SqlCommand(sql, _connection))
            {
                command.CommandText = "Select Count(1) as RecordCount from #EventIds";
                SqlDataReader reader = command.ExecuteReader();
                if (reader.HasRows)
                {
                    while (reader.Read())
                    {

                        totalInsertedRecordCount = Convert.ToInt32(reader["RecordCount"]);

                    }
                }
            }
        }
        return totalInsertedRecordCount;
    }

目前大约需要8秒钟,但我们需要让它更快。原因是我们的目标是插入900,000条记录,每条记录将分为100,000个。

你能否给我们任何暗示我们如何才能让它更完美,更快?

PS。尝试使用Dapper插入,但它不比BulkCopy快。

2 个答案:

答案 0 :(得分:0)

首先将您的列表转换为XML,如

List<int> Branches = new List<int>();
Branches.Add(1);
Branches.Add(2);
Branches.Add(3);

XElement xmlElements = new XElement("Branches", Branches.Select(i => new 
XElement("branch", i)));

然后将xml作为参数传递给SP,并将其直接插入到表中,例如:

 DECLARE @XML XML
 SET @XML = '<Branches>
<branch>1</branch>
<branch>2</branch>
<branch>3</branch>
</Branches>'

DECLARE @handle INT  
DECLARE @PrepareXmlStatus INT  

EXEC @PrepareXmlStatus= sp_xml_preparedocument @handle OUTPUT, @XML  

SELECT  * FROM    OPENXML(@handle, '/Branches/branch', 2)  
WITH (
branch varchar
)  

EXEC sp_xml_removedocument @handle 

答案 1 :(得分:0)

巴赫大小

据我所知,您尝试使用100000的BatchSize插入。更高并不总是更好。

尝试将此金额降低至5,000,并检查效果差异。

您增加了数据库往返次数,但也可能更快(此处涉及的行数太大等因素太多)

TableLock

使用SqlBulkCopyOptions.TableLock可以提高插入效果。

using (var sqlBulkCopy = new SqlBulkCopy(_connection, SqlBulkCopyOptions.KeepIdentity))