7

我正在从 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;

    }
4

6 回答 6

11

.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)");

相反,使用与用作过滤器的列的值的不同值组合相对应的键创建并填充字典。(这种相对昂贵的操作必须只执行一次,然后必须重新使用字典实例)

Dictionary<string, List<DataRow>> di = new Dictionary<string, List<DataRow>>();

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

查询字典(可能需要多个查询)以过滤行并将结果组合到列表中

string key1 = "VAL1//VAL2";
string key2 = "VAL1//<NULL>";
List<DataRow>() results = new List<DataRow>();
if (di.ContainsKey(key1))
{
    results.AddRange(di[key1]);
}
if (di.ContainsKey(key2))
{
    results.AddRange(di[key2]);
}
于 2015-10-28T08:14:51.827 回答
4

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

对于以后来的任何人......这就是我发现的。

性能对DataTable.Select(condition)您提供的“条件”的性质和结构非常敏感。这对我来说似乎是一个错误(我会在哪里向 Microsoft 报告?)但它可能只是一个怪癖。

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

  1. 定义一个包含几个简单列的数据表,如下所示:

    var dataTable = new DataTable();
    var idCol = dataTable.Columns.Add("Id", typeof(Int32));
    dataTable.Columns.Add("代码", typeof(string));
    dataTable.Columns.Add("名称", typeof(string));
    dataTable.Columns.Add("FormationDate", typeof(DateTime));
    dataTable.Columns.Add("收入", typeof(Decimal));
    dataTable.Columns.Add("ChildCount", typeof(Int32));
    dataTable.Columns.Add("外国", 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=0vs([Code] = '{searchCode}' OR 1=0)执行类似(缓慢)的时间,但是:

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

我没有调查所有场景,但似乎引入括号 - 围绕简单的比较检查冗余,或者根据需要指定子表达式优先级 - 或者“OR”的存在大大减慢了查询速度。

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

于 2018-07-31T13:48:22.257 回答
3

您可以通过使用字典来加快速度。例如:

if (distinctdt.Rows.Count > 0)
{
    // build index of C1 values to speed inner loop
    Dictionary<string, DataRow> masterIndex = new Dictionary<string, DataRow>();
    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;
于 2011-06-03T00:44:08.990 回答
2

如果在父数据表和子数据表之间创建 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/when-datatableselect-is-slow-use.html

于 2012-07-27T18:38:34.707 回答
0

你有没有通过分析器运行它?这应该是第一步。无论如何,这可能会有所帮助:

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

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

  • 现在您将字典中的所有内容都保存在内存中,只需遍历每个文件 1 次。最后通过字典/子元素并处理每一列并执行最终计算。

于 2011-06-03T03:42:47.003 回答
0

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

于 2010-07-21T11:08:59.290 回答