将包含其他数据表的ID的DataTable转换为使用其数据表名称列值替换ID的DataTable。

4
我有N个数据表,其中N-1个数据表代表一些实体,而另一个数据表代表这些实体之间的关系。
例如: 实体 国家
Country DATATABLE

ID    | Country Name | Country Code
------------------------------------
ID1   | USA          | USA
ID2   | INDIA        | IND
ID3   | CHINA        | CHI

实体洲 (Entity Continent)
Continent DATATABLE

ID     | Continent Name | Continent Code
------------------------------------
IDC1   | NORTH AMERICA  | NA
IDC2   | SOUTH AMERICA  | SA
IDC3   | ASIA           | AS

实体公司
Company DATATABLE

ID    | Company Name   | Company Code
------------------------------------
CM1   | XYZ Company    | XYZ
CM2   | Fun Company    | Fun
CM3   | ABC Company    | ABC

它们之间的关系。
Company_Country_Continent_Relationship DataTable 

ID    | Company        | Country    |  Continent     | Some Value1     | Some Value 2
-------------------------------------------------------------------------------------
R1    | CM1            | ID1        |  IDC1          | 100             | 150
R2    | CM2            | ID2        |  IDC3          | 200             | 200
R3    | CM3            | ID1        |  IDC1          | 150             | 250
R4    | CM1            | ID3        |  IDC3          | 100             | 150
R5    | CM2            | ID1        |  IDC1          | 200             | 200
R6    | CM3            | ID2        |  IDC3          | 150             | 250
R7    | CM1            | ID2        |  IDC3          | 100             | 150
R8    | CM2            | ID3        |  IDC3          | 200             | 200
R9    | CM3            | ID3        |  IDC3          | 150             | 250

现在我需要生成另一个关系表,它将保存名称而不是ID。 在这个例子中,关系数据存储了公司、国家和大陆的ID,现在我想将这些ID值转换为它们的名称,例如CM1 - XYZ公司。
为了进行这种转换,我正在使用一个名为TramnsformRelationshipData的方法,它正常工作。
    public static DataTable TramnsformRelationshipData(DataTable relationshipData, Dictionary<string, DataTable> mapping)
    {
        DataTable transformedDataTable = null;
        if (relationshipData == null || mapping == null )
           return null;

        transformedDataTable = relationshipData.Copy();

        foreach (DataColumn item in relationshipData.Columns)
        {
            if (mapping.ContainsKey(item.ColumnName))
            {
                var instanceData = mapping[item.ColumnName];
                if (instanceData == null)
                    return null;

                foreach (DataRow row in transformedDataTable.Rows)
                {
                    var filteredRows = instanceData.Select("ID = '" + row[item.ColumnName] + "'");
                    if (filteredRows.Any())
                        row[item.ColumnName] = filteredRows[0][1];
                }
            }
        }

        return transformedDataTable;
    }

但是,这种方法需要迭代所有数据表,当关系数据有更多实体需要转换时,速度非常慢。那么,我该如何优化这段代码,以处理大量具有大量行的数据表。
编辑:在大多数情况下,这些数据不存储在数据库中,而是存储在内存中,这些数据表的计数可以增加或减少。
谢谢。

如果数据已经存储在内存中,是否需要使用DataTable?将其加载到像字典这样的结构中是否更有效?如果最初是从数据库加载数据,可以使用简单快速的DataReader来填充字典。 - ElDog
3个回答

3

您是否考虑过使用SELECT INTO语句创建SQL代码(比使用C#代码更快)?当我需要处理大量数据时,我通常更喜欢使用SQL。

示例来自此MSDN页面

SELECT c.FirstName, c.LastName, e.JobTitle, a.AddressLine1, a.City, sp.Name AS [State/Province], a.PostalCode
INTO dbo.EmployeeAddresses
FROM Person.Person AS c JOIN HumanResources.Employee AS e ON e.BusinessEntityID = c.BusinessEntityID
JOIN Person.BusinessEntityAddress AS bea ON e.BusinessEntityID = bea.BusinessEntityID
JOIN Person.Address AS a ON bea.AddressID = a.AddressID
JOIN Person.StateProvince as sp ON sp.StateProvinceID = a.StateProvinceID;

首先编写一个SELECT语句来获取您的数据,然后添加INTO语句。

或者您可以使用INSERTSELECT,在其中指定要插入数据的列列表。下面是从MSDN网页中摘录的示例。

INSERT INTO Production.ZeroInventory (DeletedProductID, RemovedOnDate)
SELECT ProductID, GETDATE() FROM ...

但是,在大多数情况下,这些数据不存储在数据库中,而是存储在内存中,而在内存中,这些数据表的计数可以增加或减少。 - Yograj Gupta
你在问题中忘记提到这一点了。 - Karel Frajták

2
这里的解决方案是创建一个基于哈希的集合(比如hashtables、dictionary、.NET中的lookups),以ID列为键,并使用它来替代.Select(Id = x)。
代码可能看起来像这样...未经测试。
public static DataTable TramnsformRelationshipData(DataTable relationshipData, Dictionary<string, DataTable> mapping) 
    { 
        Dictionary<string,Dictionary<string,DataRow>> newMappings = new Dictionary<string,Dictionary<string,DataRow>>();
        foreach (var kvp in mapping)
        {
            newMappings.Add(kvp.Key,kvp.Value.Rows.Cast<DataRow>().ToDictionary(dr=>dr["ID"] as string));
        }

        DataTable transformedDataTable = null; 
        if (relationshipData == null || mapping == null ) 
           return null; 

        transformedDataTable = relationshipData.Copy(); 

        foreach (DataColumn item in relationshipData.Columns) 
        { 
            if (newMapping.ContainsKey(item.ColumnName)) 
            { 
                var instanceData = newMapping[item.ColumnName]; 
                if (instanceData == null) 
                    return null; 

                foreach (DataRow row in transformedDataTable.Rows) 
                { 
                //  var filteredRows = instanceData.Select("ID = '" + row[item.ColumnName] + "'"); 
                //  if (filteredRows.Any()) 
                    row[item.ColumnName] = instanceData[row[item.ColumnName]][1];                       
                } 
            } 
        } 

        return transformedDataTable; 
    } 

2
我认为问题本身非常简单,如果您正在使用SQL,那么3个连接就可以解决它。我猜源代码不是在SQL中(如果是的话,我建议在那里创建一个视图以获得最佳性能)。如果您必须使用数据集,可以使用LINQ来模拟连接。请参考此链接了解如何在数据集中使用LINQ:链接。您可以在此处找到如何进行连接的详细信息:这里。最终结果可能如下所示:
var q = from r in relations.AsEnumerable()
        join c in countries.AsEnumerable() on r.Country equals c.Id
        join con in continents.AsEnumerable() on r.Continent equals con.Id
        select new { someval = r.someValue1, 
                     someval2 = r.someValue2, 
                     countryname = c.Name 
                     continent = con.Name}; 

我知道如何在数据集中使用LINQ。但是数据表的数量不确定,每个数据表可以有任意数量的行和列。在问题中,我提到了N个数据表。 - Yograj Gupta
抱歉,我错过了那部分内容。您是否尝试使用以下语法在 foreach 中添加连接:var joined = q.Join(countries.AsEnumerable(), a => a.Country, b => b.Id, (a, b) => new {b.Somevalue ,a.CC1})? - Kristof

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接