[英]Convert list collection into Dictionary for performance c#
我有一些行的Excel工作表。 另外,我还有一个数据库表,它的列结构与excel表具有相同的定义。 我想做的是检查excel工作表中是否存在记录,但db表中不存在,然后将其插入名为TableReport
的新表(现有的空表)中TableReport
进行报告。
CSV档案: PathToFile.csv
原始表要做比较: FruitTable
创建用于报告的新表: TableReport
以下代码段是您可以用来测试我的方案的代码段。 下面的代码可以作为我使用一个List<T>
当PathToFile.csv
相对比较小如在样4行。 不到30秒即可完成程序的执行。 但是,我的实际情况是PathToFile.csv
大约有20万行,因此列表收集在性能方面不是那么高效。 因此,我考虑过使用Dictionary<TKey, TValue>
集合,但由于代码中的哪些部分需要调整,我陷入了困境,因为对于这两个集合,我将必须迭代整个csv来获取所有行并将其添加到在这种情况下, checkIfFruitsMatch
列表。 即使使用字典,在进行比较之前,仍然需要循环并添加它们,这已经很耗时。 在这种情况下,性能是非常关键的要求。
我尝试在具有20万行的csv上使用列表的当前实现来运行程序,这花费了15分钟以上的时间,忙着循环浏览csv并将行添加到列表中,而在终止终止之前甚至没有完成程序。
我该如何实现才能使程序更快。 执行时间不应该超过10分钟。 我已经在Windows窗体应用程序中编写了此代码。
SQL表定义:
CREATE TABLE [dbo].[FruitTable](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Apples] [nvarchar](20) NOT NULL,
[Oranges] [nvarchar](20) NOT NULL,
[Pears] [nvarchar](20) NOT NULL,
[Bananas] [nvarchar](20) NOT NULL,
[DateAddedUtc] [nvarchar](50) NULL
) ON [PRIMARY]
GO
存储过程定义:
CREATE PROC [dbo].[spAddFruitsToDB]
@Apples [nvarchar](20),
@Oranges [nvarchar](20),
@Pears [nvarchar](20),
@Bananas [nvarchar](20),
@DateAddedUtc [nvarchar](50)
AS
BEGIN
INSERT INTO TableReport --Has the same definition as FruitTable
VALUES (@Apples, @Oranges, @Pears, @Bananas, @DateAddedUtc)
END
码:
public class FruitClass {
private SqlConnection mySQLConnection;
private SqlCommand mySQLCommand;
private SqlDataReader mySQLDataReader;
private string myConnectionString;
private void CheckDataValidity()
{
Microsoft.Office.Interop.Excel.Application Excel_app = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbooks work_books = Excel_app.Workbooks;
Microsoft.Office.Interop.Excel.Workbook work_book = work_books.Open("C:\\PathToFile.csv");
Microsoft.Office.Interop.Excel.Sheets work_sheets = work_book.Worksheets;
Microsoft.Office.Interop.Excel.Worksheet work_sheet = (Microsoft.Office.Interop.Excel.Worksheet)work_sheets.get_Item(1);
List<FruitClass> checkIfFruitsMatch = new List<FruitClass>();
List<FruitClass> dbFruitsToMatch= new List<FruitClass>();
string fruitTagNumberForApples = "";
for (int j = 2; j < work_sheet.Rows.Count; j++)
{
FruitClass fruitInstance = new FruitClass();
fruitInstance.Apples = CellToString(work_sheet.Cells[j, 3]).Trim();
fruitInstance.Oranges = CellToString(work_sheet.Cells[j, 13]).Trim();
fruitInstance.Pears = CellToString(work_sheet.Cells[j, 14]).Trim();
fruitInstance.Bananas = CellToString(work_sheet.Cells[j, 15]).Trim();
fruitTagNumberForApples = fruitInstance.Apples;
checkIfFruitsMatch.Add(fruitInstance);
if (fruitTagNumberForApples == null || fruitTagNumberForApples == "" || fruitTagNumberForApples == string.Empty)
break;
}
//Get fruits in excel and do a comparison with fruits in database table Fruit
dbFruitsToMatch.Add(ReturnFruitRow());
IEnumerable<FruitClass> listComparer = checkIfFruitsMatch.Except(dbFruitsToMatch);
foreach (FruitClass i in listComparer)
{
using (var db = new DBEntities())
{
int countDBexisting = db.FruitTable.Where(x => x.Apples == i.Apples).Count();
if (countDBexisting > 0)
{
//Fruit has been previously logged. No need to insert a duplicate
}
else
{
LogFruitToDB(i, "spAddFruitsToDB"); //Insert records into a new table called "TableReport"
}
}
}
work_book.Close();
Excel_app.Quit();
}
private void LogFruitToDB(FruitClass fruitInstance, string cmdText)
{
myConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
using (mySQLConnection = new SqlConnection(myConnectionString))
{
mySQLCommand = new SqlCommand(cmdText, mySQLConnection);
mySQLCommand.CommandType = CommandType.StoredProcedure;
SqlParameter Apples_Parameter = new SqlParameter
{
ParameterName = "@Apples",
Value = fruitInstance.Apples
};
mySQLCommand.Parameters.Add(Apples_Parameter);
SqlParameter Oranges_Parameter = new SqlParameter
{
ParameterName = "@Oranges",
Value = fruitInstance.Oranges
};
mySQLCommand.Parameters.Add(Oranges_Parameter);
SqlParameter Pears_Parameter = new SqlParameter
{
ParameterName = "@Pears",
Value = fruitInstance.Pears
};
mySQLCommand.Parameters.Add(Pears_Parameter);
SqlParameter Bananas_Parameter = new SqlParameter
{
ParameterName = "@Bananas",
Value = fruitInstance.Bananas
};
mySQLCommand.Parameters.Add(Bananas_Parameter);
SqlParameter DateAddedUtc_Parameter = new SqlParameter
{
ParameterName = "@DateAddedUtc",
Value = DateTime.UtcNow.ToString()
};
mySQLCommand.Parameters.Add(DateAddedUtc_Parameter);
mySQLConnection.Open();
mySQLCommand.ExecuteNonQuery();
}
}
private FruitClass ReturnFruitRow()
{
FruitClass fruitInfo = new FruitClass();
myConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
using (mySQLConnection = new SqlConnection(myConnectionString))
{
string procedureName = "select * from dbo.FruitTable";
mySQLCommand = new SqlCommand(procedureName, mySQLConnection);
mySQLCommand.CommandType = CommandType.Text;
mySQLCommand.Connection = mySQLConnection;
mySQLCommand.Connection.Open();
mySQLDataReader = mySQLCommand.ExecuteReader();
if (mySQLDataReader.HasRows)
{
while (mySQLDataReader.Read())
{
fruitInfo.Apples = mySQLDataReader.GetString(1);
fruitInfo.Oranges = mySQLDataReader.GetString(2);
fruitInfo.Pears = mySQLDataReader.GetString(3);
fruitInfo.Bananas = mySQLDataReader.GetInt32(4).ToString();
}
}
mySQLCommand.Connection.Close();
}
return fruitInfo;
}
private string CellToString(object p)
{
try
{
return ((Microsoft.Office.Interop.Excel.Range)p).Value.ToString();
}
catch
{
return "";
}
}
}
public class FruitClass
{
public string Apples;
public string Oranges;
public string Pears;
public string Bananas;
}
注意: csv
文件作为带有列的普通.xlsx
excel文件进入,然后保存为.csv
。
但是,现在真实的场景大约有20万条记录。 还值得一提的是,该应用程序每月运行一次。
您的方法的性能将非常昂贵。 我建议您另一种方法:
1)创建一个temp_report表,该表具有要放入报表表中的行的结构。
2)在程序开始时,清空此新表
delete from dbo.temp_report
3)使用以下方法获取空的数据库
DataTable yourdatatable = new DataTable();
SqlConnection conn = new SqlConnection (connString);
SqlCommand cmd = new SqlCommand ("select * from dbo.temp_report", conn);
conn.Open ();
// create data adapter
SqlDataAdapter da = new SqlDataAdapter (cmd);
// this will be your datatable
da.Fill (yourdatatable);
conn.Close ();
4)将您的csv行插入数据表(c#)
for(here your loop on csv file)
{
row = yourdatatable.NewRow();
row["id"] = i;
row["item"] = "item " + i.ToString();
yourdatatable.Rows.Add(row);
}
5)使用批量复制方法将所有行发送到数据库:
using (SqlConnection destinationConnection = new SqlConnection (connString))
{
destinationConnection.Open ();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy (destinationConnection))
{
bulkCopy.DestinationTableName = "dbo.temp_report";
try
{
// Write from the source to the destination.
bulkCopy.WriteToServer (yourdatatable);
}
catch (Exception ex)
{
Console.WriteLine (ex.Message);
}
}
}
6)通过进行区别查询(而不是通过C#代码),将TableTable表中未存在的行插入TableReport表中
7)请注意,您还可以通过读取csv文件文本(在分隔符(选项卡或取决于文件)上进行拆分)来提高性能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.