【问题标题】:More efficient way of assigning values in DataTable?在 DataTable 中分配值的更有效方法?
【发布时间】:2012-12-26 16:34:17
【问题描述】:

我有一个包含两列的 DataTable:JobDetailID 和 CalculatedID。 JobDetailID 并不总是唯一的。我希望给定 JobDetailID 的一个/第一个 CalculatedID 实例是 JobDetailID +“A”,并且当有多行具有相同的 JobDetailID 时,我希望连续的行是 JobDetailID +“B”、“C”等。具有相同 JobDetailID 的行不超过四五行。

我目前的实现方式如下,但速度慢得让人无法接受:

private void AddCalculatedID(DataTable data)
{
    var calculatedIDColumn = new DataColumn { DataType = typeof(string), ColumnName = "CalculatedID" };
    data.Columns.Add(calculatedIDColumn);
    data.Columns["CalculatedID"].SetOrdinal(0);

    var enumerableData = data.AsEnumerable();

    foreach (DataRow row in data.Rows)
    {
        var jobDetailID = row["JobDetailID"].ToString();

        // Give calculated ID of JobDetailID + A, B, C, etc. for multiple rows with same JobDetailID
        int x = 65; // ASCII value for A
        string calculatedID = jobDetailID + (char)x;

        while (string.IsNullOrEmpty(row["CalculatedID"].ToString()))
        {
            if ((enumerableData
                .Any(r => r.Field<string>("CalculatedID") == calculatedID)))
            {
                calculatedID = jobDetailID + (char)x;
                x++;
            }
            else
            {
                row["CalculatedID"] = calculatedID;
                break;
            }
        }
    }
}

假设我需要遵守这种输出格式,我该如何提高这种性能?

【问题讨论】:

  • 是否可以在获取数据的 proc 中进行计算?可能不是最优雅的解决方案,但您正在遍历结果集以添加一列。也许最好在 proc 中进行计算,然后避免上面的代码。也许更好的第一个问题是——你是如何获取数据的?
  • 首先你是如何填充DataTable 的?如果它不在数据库中,我建议在那里计算此列。
  • @SPFiredrake 的答案非常易读(我通常更喜欢非关键性能),但它不是“最快的”。让我以更快的方法工作并将其作为建议的答案。
  • 我刚刚放了一个我认为“更快”的版本,它仍然有根据尚未发现的需求进行优化的空间。虽然,这根本不使用 LINQ(这很好,如果您已经知道所需的逻辑,使用自定义代码会更快)

标签: c# performance linq datatable


【解决方案1】:

最好在您获取数据的地方添加生成CalculatedID的代码,但是,如果不可用,您可能希望避免每次发现重复时扫描整个表。您可以使用 Dictionary 来表示已使用的密钥,如下所示:

private void AddCalculatedID(DataTable data)
{
    var calculatedIDColumn = new DataColumn { DataType = typeof(string), ColumnName = "CalculatedID" };
    data.Columns.Add(calculatedIDColumn);
    data.Columns["CalculatedID"].SetOrdinal(0);

    Dictionary<string, string> UsedKeyIndex = new Dictionary<string, string>();

    foreach (DataRow row in data.Rows)
    {
        string jobDetailID = row["JobDetailID"].ToString();
        string calculatedID;

        if (UsedKeyIndex.ContainsKey(jobDetailID))
        {
          calculatedID = jobDetailID + 'A';
          UsedKeyIndex.Add(jobDetailID, 'A');
        }
        else
        {
           char nextKey = UsedKeyIndex[jobDetailID].Value+1;
           calculatedID = jobDetailID + nextKey;
           UsedKeyIndex[jobDetailID] = nextKey;
        }

        row["CalculatedID"] = calculatedID;
    }
}

这实际上会以内存换取速度,因为它将缓存所有使用的 JobDetailID 以及用于生成键的最后一个字符。如果您有大量这些 JobDetailID,这可能会占用大量内存,但我怀疑除非您有数百万行要处理,否则您会遇到问题。

【讨论】:

    【解决方案2】:

    如果我理解您关于为行设置 CalculatedID 的想法,那么下面的算法就可以解决问题,而且它的复杂性是线性的。最重要的部分是data.Select("","JobDetailID"),在那里我得到了一个排序的行列表。 不是我自己编译的,所以可能有语法错误。

    private void AddCalculatedID(DataTable data)
    {
        var calculatedIDColumn = new DataColumn { DataType = typeof(string), ColumnName = "CalculatedID" };
        data.Columns.Add(calculatedIDColumn);
        data.Columns["CalculatedID"].SetOrdinal(0);
    
        int jobDetailID = -1;
        int letter = 65;
        foreach (DataRow row in data.Select("","JobDetailID"))
        {
            if((int)row["JobDetailID"] == jobDetailID)
            {
                row["CalculatedID"] = row["JobDetailID"].ToString() + (char)letter;
                letter++;
            }
            else
            {
                letter = 65;
                jobDetailID = (int)row["JobDetailID"];
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      您将此标记为 LINQ,但您使用的是迭代方法。可能最好的方法是结合使用两者,遍历每个“分组”并为分组中的每一行分配计算的 ID。

      foreach (var groupRows in data.AsEnumerable().GroupBy(d => d["JobDetailID"].ToString()))
      {
          if(string.IsNullOrEmpty(groupRows.Key))
              continue;
      
          // We now have each "grouping" of duplicate JobDetailIDs.
          int x = 65; // ASCII value for A
          foreach (var duplicate in groupRows)
          {
              string calcID = groupRows.Key + ((char)x++);
              duplicate["CalculatedID"] = calcID;
              //Can also do this and achieve same results.
              //duplicate["CalculatedID"] = groupRows.Key + ((char)x++);
          }
      }
      

      您要做的第一件事是对将有重复的列进行分组。您将遍历这些分组中的每一个,并为每个分组重置后缀值。对于分组中的每一行,您将获取计算的 ID(同时增加后缀值)并将 ID 分配回重复的行。作为旁注,我们正在更改我们在此处列举的项目,这通常是一件坏事。但是,我们正在更改与我们的枚举声明 (GroupBy) 无关的数据,因此它不会改变我们的枚举行为。

      【讨论】:

        【解决方案4】:

        此方法一次性完成工作。例如,如果“JobDetailID”是整数而不是字符串,或者 DataTable 始终接收按“JobDetailID”排序的数据(您可以摆脱字典),您可以进一步优化它,但这里有一个草稿:

            private static void AddCalculatedID(DataTable data)
            {
                data.BeginLoadData();
        
                try
                {
                    var calculatedIDColumn = new DataColumn { DataType = typeof(string), ColumnName = "CalculatedID" };
                    data.Columns.Add(calculatedIDColumn);
                    data.Columns["CalculatedID"].SetOrdinal(0);
        
                    var jobDetails = new Dictionary<string, int>(data.Rows.Count);
        
                    foreach (DataRow row in data.Rows)
                    {
                        var jobDetailID = row["JobDetailID"].ToString();
                        int lastSuffix;
        
                        if (jobDetails.TryGetValue(jobDetailID, out lastSuffix))
                        {
                            lastSuffix++;
                        }
                        else
                        {
                            // ASCII value for A
                            lastSuffix = 65;
                        }
        
                        row["CalculatedID"] = jobDetailID + (char)lastSuffix;
                        jobDetails[jobDetailID] = lastSuffix;
                    }
                }
                finally
                {
                    data.EndLoadData();
                }
            }
        

        【讨论】:

          猜你喜欢
          • 2017-04-17
          • 2021-01-29
          • 2016-02-05
          • 2017-09-13
          • 2018-05-01
          • 1970-01-01
          • 2019-02-12
          • 1970-01-01
          • 2014-09-11
          相关资源
          最近更新 更多