21

I have these two datatables and I want to get the difference between them. Here is an example:

Table1
-------------------------
ID  |   Name 
--------------------------
 1  |  A
 2  |  B
 3  |  C
--------------------------

Table2
-------------------------
ID  |   Name 
--------------------------
 1  |  A
 2  |  B
--------------------------

I just want the result as data which is in table1 and not in table2 (table1-table2)

ResultTable
-------------------------
ID  |   Name 
--------------------------
 3  |  C
--------------------------

I tried to use these two similar solutions via Linq, but it always return table1 and not table1-table2. Here is first solution:

DataTable table1= ds.Tables["table1"];
DataTable table2= ds.Tables["table2"];
var diff= table1.AsEnumerable().Except(table2.AsEnumerable(),DataRowComparer.Default);

Second solution:

var dtOne = table1.AsEnumerable();
var dtTwo = table2.AsEnumerable();
var difference = dtOne.Except(dtTwo);

So, where is the mistake? Thank you a lot for all your answers. :)

Otiel
  • 18,404
  • 16
  • 78
  • 126
user2095405
  • 347
  • 1
  • 4
  • 15
  • 2
    Except checks wether or not they are the same instance. Not wether their respective properties are identical. You can use the overload that accepts an EqualityComparer or you can investigate the extension method ExceptBy() that is implemented in various LINQ+ libraries, also in Jon Skeets MoreLinq (http://code.google.com/p/morelinq/) – Tormod Feb 21 '13 at 12:52
  • 2
    @Tormod, but then what is the use of the `DataRowComparer` in his first solution? It overrides `public bool Equals(TRow leftRow, TRow rightRow)` to compare actual column values. – hometoast Feb 21 '13 at 13:05
  • 2
    Can you show how you are retrieving your datatables? Is it possible the data is different than you expect it to be? I did a quick sample in LINQPad and your first solution seems to work fine. – goric Feb 21 '13 at 13:13
  • 2
    @Tormod, the msdn library says otherwise: The DataRowComparer class is used to compare the values of the DataRow objects and does not compare the object references. (http://msdn.microsoft.com/en-us/library/system.data.datarowcomparer.aspx) – bouvierr Feb 21 '13 at 13:22
  • So the question is: why did the "first solution" not work? Nothing seems wrong with it. – Gert Arnold Aug 17 '23 at 08:14

8 Answers8

7

You can try the following code...

table1.AsEnumerable().Where(
    r =>!table2.AsEnumerable().Select(x=>x["ID"]).ToList().Contains(r["ID"])).ToList();
Amol Kolekar
  • 2,307
  • 5
  • 30
  • 45
7

I just went through this and wanted to share my findings. For my application it is a data sync mechanism, but i think you will see how this applies to the original question.

In my case, I had a DataTable that represented my last data upload and sometime in the future, I need to get the current state of the data and only upload the differences.

//  get the Current state of the data
DataTable dtCurrent = GetCurrentData();

//  get the Last uploaded data
DataTable dtLast = GetLastUploadData();
dtLast.AcceptChanges();

//  the table meant to hold only the differences
DataTable dtChanges = null;

//  merge the Current DataTable into the Last DataTable, 
//  with preserve changes set to TRUE
dtLast.Merge(dtCurrent, true);

//  invoke GetChanges() with DataRowState.Unchanged
//    !! this is the key !!
//    the rows with RowState == DataRowState.Unchanged 
//    are the differences between the 2 tables
dtChanges = dtLast.GetChanges(DataRowState.Unchanged);

I hope this helps. I fought with this for a few hours, and found lots of false-leads on the interwebz, and ended up comparing RowStates after merging a few different ways

Kevin.Hardy
  • 71
  • 1
  • 2
1

I will try to do it on a column level rather than a DataTable.

IEnumerable<int> id_table1 = table1.AsEnumerable().Select(val=> (int)val["ID"]);
IEnumerable<int> id_table2  = table2.AsEnumerable().Select(val=> (int)val["ID"]);
IEnumerable<int> id_notinTable1= id_table2.Except(id_table1);

Just adding a .Select() to your answer...

skjcyber
  • 5,759
  • 12
  • 40
  • 60
1

Try this

DataTable dtmismatch = Table1.AsEnumerable().Except(Table2.AsEnumerable(), DataRowComparer.Default).CopyToDataTable<DataRow>();
Madhu
  • 479
  • 4
  • 10
1

You are using LINQ's Except method seems correct, but the issue might be related to the default equality comparer used for DataRow objects. The default equality comparer for DataRow objects might not work as expected for your case because it compares all columns of the DataRow, not just the "ID" and "Name" columns that you want to use for comparison. You can use the LINQ Except method with a custom equality comparer. Here's how you can do it:

DataTable table1 = ds.Tables["table1"];
DataTable table2 = ds.Tables["table2"];

var comparer = new CustomDataRowComparer(); // Custom equality comparer
var diff = table1.AsEnumerable().Except(table2.AsEnumerable(), comparer);
DataTable resultTable = diff.CopyToDataTable();

class CustomDataRowComparer : IEqualityComparer<DataRow>
{
    public bool Equals(DataRow x, DataRow y)
    {
        // Compare based on specific columns (ID and Name in this case)
        return x["ID"].Equals(y["ID"]) && x["Name"].Equals(y["Name"]);
    }

    public int GetHashCode(DataRow obj)
    {
        // Generate a hash code based on specific columns (ID and Name in this case)
        int hash = obj["ID"].GetHashCode() ^ obj["Name"].GetHashCode();
        return hash;
    }
}
Amit Mohanty
  • 387
  • 1
  • 9
  • "Try this" isn't really an answer. – Gert Arnold Dec 21 '18 at 09:19
  • Still just "try this". Answers always require a brief explanation. But also note that you just took the idea of [this answer](https://stackoverflow.com/a/32247066/861716), which is not a fair thing to do. But worse, it's exactly what OP already tried in the "first solution". What every answerer should have done here is ask why it doesn't seem to work, because seemingly it should. – Gert Arnold Aug 17 '23 at 08:12
  • @GertArnold: now you find a explanation in my posted answer of the above issue. Hopefully it will help you. – Amit Mohanty Aug 17 '23 at 09:18
  • The last version of the answer at least works toward a solution of an issue that was never clarified but probably was going on. – Gert Arnold Aug 17 '23 at 19:30
0

Try the below approach:

Initialization:

var columnId = new DataColumn("ID", typeof (int));
var columnName = new DataColumn("Name", typeof (string));
var table1 = new DataTable();
table1.Columns.AddRange(new[] {columnId, columnName});
table1.PrimaryKey = new[] {columnId};
table1.Rows.Add(1, "A");
table1.Rows.Add(2, "B");
table1.Rows.Add(3, "C");

var table2 = table1.Clone();
table2.Rows.Add(1, "A");
table2.Rows.Add(2, "B");
table2.Rows.Add(4, "D");

Solution:

var table3 = table1.Copy();
table3.AcceptChanges();
table3.Merge(table2);

var distinctRows = from row in table3.AsEnumerable()
                   where row.RowState != DataRowState.Modified
                   select row;

var distintTable = distinctRows.CopyToDataTable();

Above solution also works when there are new rows in table2 that were not present in table1.

distintTable constains C and D.

Ryszard Dżegan
  • 24,366
  • 6
  • 38
  • 56
0

Try below, this is pretty basic. Merge two sets together, and get the difference. If the sets dont align up properly, then this will not work.

DataSet firstDsData = new DataSet();
DataSet secondDsData = new DataSet();
DataSet finalDsData = new DataSet();
DataSet DifferenceDataSet = new DataSet();
finalDsData.Merge(firstDsData);
finalDsData.AcceptChanges();
finalDsData.Merge(secondDsData);
DifferenceDataSet = finalDsData.GetChanges();
TGarrett
  • 562
  • 4
  • 15
0

Try below, this is pretty basic. Merge two sets together, and get the difference. If the sets dont align up properly, then this will not work. Trying to Test the same

DataSet firstDsData = new DataSet();
DataSet secondDsData = new DataSet();
DataSet finalDsData = new DataSet();
DataSet DifferenceDataSet = new DataSet();
finalDsData.Merge(firstDsData);
finalDsData.AcceptChanges();
finalDsData.Merge(secondDsData);
DifferenceDataSet = finalDsData.GetChanges();