比较两个数据表以确定其中一个而不是另一个的行
我有两个由CSV文件生成的数据表," A"和" B"。我需要能够检查B中存在的行,而A中不存在的行。
有没有一种方法可以执行某种查询以显示不同的行,或者我必须遍历每个DataTable的每一行以检查它们是否相同?如果表变大,则后一种选择似乎非常密集。
解决方案
我们可以使用DataTable上的Merge和GetChanges方法来执行此操作:
A.Merge(B); // this will add to A any records that are in B but not A return A.GetChanges(); // returns records originally only in B
仅供参考:
一般来说,关于算法,比较两组可排序的对象(通常是id)不是O(M * N / 2)运算,而是将O(M + N)排序(如果将两组排序)。因此,我们使用指向另一个表的开始的指针扫描了一个表,然后:
other_item= A.first() only_in_B= empty_list() for item in B: while other_item > item: other_item= A.next() if A.eof(): only_in_B.add( all the remaining B items) return only_in_B if item < other_item: empty_list.append(item) return only_in_B
上面的代码显然是伪代码,但是如果我们决定自己编写代码,则应该给我们基本的指导。
would I have to iterate through each row on each DataTable to check if they are the same.
从CSV文件中加载数据后,我们将不会有任何索引或者任何内容,因此在某些时候,无论是代码还是库,都需要遍历每一行。 , 管他呢。
无论如何,这是一个算法问题,这不是我的专长,但是我的幼稚方法如下:
1:我们可以利用数据的任何属性吗?每个表中的所有行是否都是唯一的,是否可以按照相同的条件对它们进行排序?如果是这样,我们可以执行以下操作:
- 通过两个表的ID对它们进行排序(使用一些有用的东西,例如快速排序)。如果它们已经被排序,那么我们赢了很多。
- 一次浏览两个表,跳过任何一个表中ID的任何空白。匹配ID的平均重复记录。
这样一来,我们可以(sort time * 2)+一遍操作,因此,如果我的big-O表示法正确,则应该是(whatever-sort-time)+ O(m + n),这非常好。
(修订:这是描述的方法)
- 遍历表1,对于每一行,将其ID(或者计算出的哈希码,或者该行的某些其他唯一ID)粘贴到字典(或者哈希表,如果我们愿意称呼它)中。
- 遍历表2,并针对每一行,查看字典中是否存在ID(或者哈希码等)。我们正在利用字典的运行速度非常快的事实-我认为O(1)吗?抬头。这一步确实非常快,但是我们需要为所有这些字典插入付出代价。
2:一种替代方法,其效率或者多或者少取决于数据量:
我真的很想知道哪些人比我更了解算法,所以提出了一个:-)
IEnumerable<string> idsInA = tableA.AsEnumerable().Select(row => (string)row["ID"]); IEnumerable<string> idsInB = tableB.AsEnumerable().Select(row => (string)row["ID"]); IEnumerable<string> bNotA = idsInB.Except(idsInA);
假设我们在此示例中具有一个适当类型的ID列(即提供哈希码并实现相等性)字符串,该字符串为伪代码,因为我对DataTables并不熟悉并且没有时间查找所有内容现在 :)
到目前为止的答案都假设我们只是在寻找重复的主键。例如,这是一个非常简单的问题,我们可以使用Merge()方法。
但是我理解问题意味着我们正在寻找重复的DataRows。 (从问题描述出发,两个表都是从CSV文件导入的,我什至假设原始行没有主键值,并且在导入期间通过AutoNumber分配了任何主键。)
天真的实现(对于A中的每一行,将其ItemArray与B中的每一行进行比较)确实会在计算上昂贵。
一种更便宜的方法是使用散列算法。对于每个DataRow,将其列的字符串值连接为单个字符串,然后对该字符串调用GetHashCode()以获取一个int值。为数据表B中的每个DataRow创建一个包含字典的项,在字典中键入一个Dictionary <int,DataRow>。然后,为数据表A中的每个DataRow计算哈希码,并查看其是否包含在字典中。如果不是,我们就会知道DataRow在DataTable B中不存在。
这种方法有两个弱点,这两个弱点都是由于两个字符串可能不相等而产生相同的哈希码这一事实而出现的。如果我们在A中找到一行,其哈希值在字典中,则需要检查字典中的DataRow以验证两行是否真正相等。
第二个弱点更为严重:B中的两个不同的DataRows不可能(但可能)散列为相同的键值。出于这个原因,该词典实际上应该是Dictionary <int,List <DataRow >>
,并且我们应该针对列表中的每个DataRow执行上一段所述的检查。
要使此功能正常运行,需要花费大量的工作,但是它是一种O(m + n)算法,我认为它会变得更好。
感谢所有反馈。
不幸的是,我没有任何索引。我将提供一些有关我的情况的更多信息。
我们有一个报告程序(替换为Crystal报告),该报告程序已安装在整个EU的7台服务器中。这些服务器上有许多报告(每个国家/地区的报告不尽相同)。它们由使用XML文件进行配置的命令行应用程序调用。因此,一个XML文件可以调用多个报告。
命令行应用程序是由我们的通宵流程安排和控制的。因此,可以从多个位置调用XML文件。
CSV的目标是生成所有正在使用的报告以及从何处调用报告的列表。
我正在遍历所有引用的XML文件,查询调度程序并生成所有报告的列表。 (这还不错)。
我的问题是我必须保留可能已从生产环境中删除的所有报告的列表。因此,我需要将旧的CSV与新的数据进行比较。为此,我认为最好将其放入DataTables并比较信息(这可能是错误的方法。我想我可以创建一个保存它的对象并比较其差异,然后遍历它们的对象)。
我关于每个报告的数据如下:
字符串任务名称
字符串动作名称
Int ActionID(动作ID可以在多个记录中,因为单个动作可以调用许多报告,即XML文件)。
字符串XML文件称为
字符串报告名称
我将尝试MusiGenesis提出的Merge想法(谢谢)。 (重读了一些帖子,不确定是否可以合并,但是值得一试,因为我之前没有听说过,所以有一些新的知识需要学习。
HashCode想法听起来也很有趣。
public DataTable compareDataTables(DataTable First, DataTable Second) { First.TableName = "FirstTable"; Second.TableName = "SecondTable"; //Create Empty Table DataTable table = new DataTable("Difference"); DataTable table1 = new DataTable(); try { //Must use a Dataset to make use of a DataRelation object using (DataSet ds4 = new DataSet()) { //Add tables ds4.Tables.AddRange(new DataTable[] { First.Copy(), Second.Copy() }); //Get Columns for DataRelation DataColumn[] firstcolumns = new DataColumn[ds4.Tables[0].Columns.Count]; for (int i = 0; i < firstcolumns.Length; i++) { firstcolumns[i] = ds4.Tables[0].Columns[i]; } DataColumn[] secondcolumns = new DataColumn[ds4.Tables[1].Columns.Count]; for (int i = 0; i < secondcolumns.Length; i++) { secondcolumns[i] = ds4.Tables[1].Columns[i]; } //Create DataRelation DataRelation r = new DataRelation(string.Empty, firstcolumns, secondcolumns, false); ds4.Relations.Add(r); //Create columns for return table for (int i = 0; i < First.Columns.Count; i++) { table.Columns.Add(First.Columns[i].ColumnName, First.Columns[i].DataType); } //If First Row not in Second, Add to return table. table.BeginLoadData(); foreach (DataRow parentrow in ds4.Tables[0].Rows) { DataRow[] childrows = parentrow.GetChildRows(r); if (childrows == null || childrows.Length == 0) table.LoadDataRow(parentrow.ItemArray, true); table1.LoadDataRow(childrows, false); } table.EndLoadData(); } } catch (Exception ex) { Console.WriteLine(ex.Message); } return table; }
try { if (ds.Tables[0].Columns.Count == ds1.Tables[0].Columns.Count) { for (int i = 0; i < ds.Tables[0].Rows.Count; i++) { for (int j = 0; j < ds.Tables[0].Columns.Count; j++) { if (ds.Tables[0].Rows[i][j].ToString() == ds1.Tables[0].Rows[i][j].ToString()) { } else { MessageBox.Show(i.ToString() + "," + j.ToString()); } } } } else { MessageBox.Show("Table has different columns "); } } catch (Exception) { MessageBox.Show("Please select The Table"); }
段落数量不匹配