C#中的DataTable.Select和Performance问题

我正在从DataTables中的三个制表符分隔文件导入数据,然后我需要遍历主表的每一行并找到两个子表中的所有行。 对于我从子表中找到的每个DataRow []数组,我必须再次单独遍历每一行并根据不同的参数检查值,最后我需要创建一个最终记录,这将是主人和两个孩子的合并表列。 现在我已经完成了它的工作,但问题在于它的性能。 我正在使用DataTable.Select查找子表中的所有子行,我认为这些行非常慢。 请记住表中没有任何主键,因为重复行是可接受的。 目前我在主表中有1200行,在子表中有8000行,这样做的总时间是8分钟。

知道怎样才能提高性能。 提前致谢

代码低于***************

DataTable rawMasterdt = importMasterFile(); DataTable rawDespdt = importDescriptionFile(); dsHelper = new DataSetHelper(); DataTable distinctdt = new DataTable(); distinctdt = dsHelper.SelectDistinct("DistinctOffers", rawMasterdt, "C1"); if (distinctdt.Rows.Count > 0) { int count = 0; foreach (DataRow offer in distinctdt.Rows) { string exp = "C1 = " + "'" + offer[0].ToString() + "'" + ""; DataRow masterRow = rawMasterdt.Select(exp)[0]; count++; txtBlock1.Text = "Importing Offer " + count.ToString() + " of " + distinctdt.Rows.Count.ToString(); if (masterRow != null ) { Product newProduct = new Product(); newProduct.Code = masterRow["C4"].ToString(); newProduct.Name = masterRow["C5"].ToString(); // ----- newProduct.Description = getProductDescription(offer[0].ToString(), rawDespdt); newProduct.Weight = getProductWeight(offer[0].ToString(), rawDespdt); newProduct.Price = getProductRetailPrice(offer[0].ToString(), rawDespdt); newProduct.UnitPrice = getProductUnitPrice(offer[0].ToString(), rawDespdt); // ------- more functions similar to above here productList.Add(newProduct); } } txtBlock1.Text = "Import Completed"; public string getProductDescription(string offercode, DataTable dsp) { string exp = "((C1 = " + "'" + offercode + "')" + " AND ( C6 = 'c' ))"; DataRow[] dRows = dsp.Select( exp); string descrip = ""; if (dRows.Length > 0) { for (int i = 0; i < dRows.Length - 1; i++) { descrip = descrip + " " + dRows[i]["C12"]; } } return descrip; } 

.Net 4.5,问题仍然存在。

以下是一个简单基准测试的结果,其中DataTable.Select和不同的字典实现比较CPU时间(结果以毫秒为单位)

  #Rows Table.Select Hashtable[] SortedList[] Dictionary[] 1000 43,31 0,01 0,06 0,00 6000 291,73 0,07 0,13 0,01 11000 604,79 0,04 0,16 0,02 16000 914,04 0,05 0,19 0,02 21000 1279,67 0,05 0,19 0,02 26000 1501,90 0,05 0,17 0,02 31000 1738,31 0,07 0,20 0,03 

问题:

DataTable.Select方法在内部创建“System.Data.Select”类实例,此“Select”类根据查询中指定的字段(列)创建索引。 Select类重新使用它创建的索引,但DataTable实现不会重用Select类实例,因此每次调用DataTable.Select时都会重新创建索引。 (通过反编译System.Data可以观察到此行为)

解:

假设以下查询

 DataRow[] rows = data.Select("COL1 = 'VAL1' AND (COL2 = 'VAL2' OR COL2 IS NULL)"); 

而是使用与用作filter的列的值的不同值组合对应的键创建和填充字典。 (这个相对昂贵的操作必须只进行一次,然后必须重新使用字典实例)

 Dictionary> di = new Dictionary>(); foreach (DataRow dr in data.Rows) { string key = (dr["COL1"] == DBNull.Value ? "" : dr["COL1"]) + "//" + (dr["COL2"] == DBNull.Value ? "" : dr["COL2"]); if (di.ContainsKey(key)) { di[key].Add(dr); } else { di.Add(key, new List()); di[key].Add(dr); } } 

查询字典(可能需要多个查询)来过滤行并将结果合并到List中

 string key1 = "VAL1//VAL2"; string key2 = "VAL1//"; List() results = new List(); if (di.ContainsKey(key1)) { results.AddRange(di[key1]); } if (di.ContainsKey(key2)) { results.AddRange(di[key2]); } 

使用字典可以加快速度。 例如:

 if (distinctdt.Rows.Count > 0) { // build index of C1 values to speed inner loop Dictionary masterIndex = new Dictionary(); foreach (DataRow row in rawMasterdt.Rows) masterIndex[row["C1"].ToString()] = row; int count = 0; foreach (DataRow offer in distinctdt.Rows) { 

然后代替

  string exp = "C1 = " + "'" + offer[0].ToString() + "'" + ""; DataRow masterRow = rawMasterdt.Select(exp)[0]; 

你会这样做的

 DataRow masterRow; if (masterIndex.ContainsKey(offer[0].ToString()) masterRow = masterIndex[offer[0].ToString()]; else masterRow = null; 

如果在父DataTables和子DataTables之间创建DataRelation,则可以通过在父行上调用DataRow.GetChildRows(DataRelation)来查找子行(如果是类型化的DataSet,则调用DataRow.GetChildRelName)。 搜索将应用TreeMap查找,即使有很多子行,性能也应该很好。

如果您必须根据其他条件而不是DataRelation的外键搜索行,我建议您使用DataView.Sort / DataView.FindRows()而不是DataTable.Select(),只要您需要查询更多数据不止一次。 DataView.FindRows()将基于TreeMap查找(O(log(N)),其中DataTable.Select()必须扫描所有行(O(N))。本文包含更多详细信息: http:// arnosoftwaredev .blogspot.com / 2011/02 /时,datatableselect-是慢,use.html

可以使DataTable与DataSet中的其他DataTable具有关系。 请参阅http://msdn.microsoft.com/en-us/library/ay82azad%28VS.71%29.aspx进行一些讨论并作为浏览的起点。 我没有太多使用它们的经验,但据我所知,它们会做你想要的(假设你的表格是合适的格式)。 我认为这些效率高于手动过程,但我可能错了。 如果他们为您工作并进行基准测试以确定它们是否有所改善,那么可能值得一看……

你是通过探查器运行的吗? 这应该是第一步。 无论如何,这可能会有所帮助:

  • 逐行将主文本文件读入内存。 将主记录作为键放入字典中。 将其添加到数据集(1通过主数据集)。

  • 逐行读取子文本文件,将其添加为上面创建的字典中相应主记录的值

  • 现在你在字典中的所有东西都在内存中,只有1个遍历每个文件。 最后通过字典/子项并处理每个列并执行最终计算。

我知道这是一个老问题,支持这个问题的代码可能已经改变,但我最近遇到过这个问题(并且已经深入了解)。

对于以后出现的人来说……这就是我找到的。

DataTable.Select(condition)性能对您提供的“条件”的性质和结构非常敏感。 这看起来像是一个错误(我会把它报告给微软?)但它可能只是一个怪癖。

我编写了一组测试来演示结构如下的问题:

  1. 使用几个简单的列定义数据表,如下所示:

    var dataTable = new DataTable();
    var idCol = dataTable.Columns.Add(“Id”,typeof(Int32));
    dataTable.Columns.Add(“Code”,typeof(string));
    dataTable.Columns.Add(“Name”,typeof(string));
    dataTable.Columns.Add(“FormationDate”,typeof(DateTime));
    dataTable.Columns.Add(“收入”,typeof(十进制));
    dataTable.Columns.Add(“ChildCount”,typeof(Int32));
    dataTable.Columns.Add(“Foreign”,typeof(Boolean));
    dataTable.PrimaryKey = new DataColumn [1] {idCol};

  2. 使用40000条记录填充表格,每条记录都有一个唯一的“代码”字段。

  3. 使用两个相似但格式不同的查询对数据表执行一批“选择”(每个具有不同的参数)并记录并比较两种格式中每种格式所花费的总时间。

你会得到非凡的结果。 例如,并排测试以下两个条件:

Q1:[代码] =’XX’

Q2 🙁 [代码] =’XX’)

[我使用上面两个查询做多个Select调用,每次迭代我用数据表中存在的有效代码替换XX]结果?

320次查询与40000条记录的时间比较:180毫秒总搜索时间没有括号,6871毫秒搜索总搜索时间用括号

是的 – 如果您只是在条件周围有额外的括号,则慢38倍。 还有其他情况有不同的反应。

例如, [Code] = '{searchCode}' OR 1=0 vs ([Code] = '{searchCode}' OR 1=0)执行相似(慢)次,但是:

[Code] = '{searchCode}' AND 1=1 vs ([Code] = '{searchCode}' AND 1=1)再次显示非括号内的版本接近40倍。

我没有调查所有场景,但似乎括号的引入 – 无论是围绕简单的比较检查,还是根据需要指定子表达式优先级 – 或者存在’OR’都会大大减慢查询速度。

我可以推测这个问题是由数据表如何解析你使用的条件以及它如何创建和使用内部索引引起的…但我不会。