Combining n DataTables into a Single DataTable - c#

All, there are some question on this, but I can't seem to extract enough information to solve the problem for my case. I extract an unknown number of tables into SQL Server 'Tab1', 'Tab2', 'Tab3', ... , 'TabN'. The columns in these tables are different, but the row definitions are the same. I need to pull all the data in from the Server into N DataTables and then combine these to form a single DataTable. What I do currently is
int nTmpVolTabIdx = 1;
strSqlTmp = String.Empty;
using (DataTable dataTableALL = new DataTable())
{
while (true)
{
string strTmpVolName = String.Format("Tab{0}", nTmpVolTabIdx);
strSqlTmp = String.Format("SELECT * FROM [{0}];", strTmpVolName);
// Pull the data from 'VolX' into a local DataTable.
using (DataTable dataTable = UtilsDB.DTFromDB(conn, strTmpVolName, strSqlTmp, false))
{
if (dataTable == null)
break;
else
dataTableALL.Merge(dataTable);
}
nTmpVolTabIdx++;
}
...
}
This merges the DataTables but they are miss-aligned (padding blank cells onto the appended data set). I could append the columns of the new DataTable via a loop; but is there an easier/nicer way to do this (perhaps using LINQ)?
Thanks for your time.
Edit. To provide the example data sets.
What I required is
The individual tables are
After the first Merge operation I have the following
Thanks again.

The table has repeating primary keys after the Merge because no primary-key was defined. So either specify the PK or try this method here which i've written from scratch(so it's not really tested):
public static DataTable MergeAll(this IList<DataTable> tables, String primaryKeyColumn)
{
if (!tables.Any())
throw new ArgumentException("Tables must not be empty", "tables");
if(primaryKeyColumn != null)
foreach(DataTable t in tables)
if(!t.Columns.Contains(primaryKeyColumn))
throw new ArgumentException("All tables must have the specified primarykey column " + primaryKeyColumn, "primaryKeyColumn");
if(tables.Count == 1)
return tables[0];
DataTable table = new DataTable("TblUnion");
table.BeginLoadData(); // Turns off notifications, index maintenance, and constraints while loading data
foreach (DataTable t in tables)
{
table.Merge(t); // same as table.Merge(t, false, MissingSchemaAction.Add);
}
table.EndLoadData();
if (primaryKeyColumn != null)
{
// since we might have no real primary keys defined, the rows now might have repeating fields
// so now we're going to "join" these rows ...
var pkGroups = table.AsEnumerable()
.GroupBy(r => r[primaryKeyColumn]);
var dupGroups = pkGroups.Where(g => g.Count() > 1);
foreach (var grpDup in dupGroups)
{
// use first row and modify it
DataRow firstRow = grpDup.First();
foreach (DataColumn c in table.Columns)
{
if (firstRow.IsNull(c))
{
DataRow firstNotNullRow = grpDup.Skip(1).FirstOrDefault(r => !r.IsNull(c));
if (firstNotNullRow != null)
firstRow[c] = firstNotNullRow[c];
}
}
// remove all but first row
var rowsToRemove = grpDup.Skip(1);
foreach(DataRow rowToRemove in rowsToRemove)
table.Rows.Remove(rowToRemove);
}
}
return table;
}
You can call it in this way:
var tables = new[] { tblA, tblB, tblC };
DataTable TblUnion = tables.MergeAll("c1");
Used this sample data:
var tblA = new DataTable();
tblA.Columns.Add("c1", typeof(int));
tblA.Columns.Add("c2", typeof(int));
tblA.Columns.Add("c3", typeof(string));
tblA.Columns.Add("c4", typeof(char));
var tblB = new DataTable();
tblB.Columns.Add("c1", typeof(int));
tblB.Columns.Add("c5", typeof(int));
tblB.Columns.Add("c6", typeof(string));
tblB.Columns.Add("c7", typeof(char));
var tblC = new DataTable();
tblC.Columns.Add("c1", typeof(int));
tblC.Columns.Add("c8", typeof(int));
tblC.Columns.Add("c9", typeof(string));
tblC.Columns.Add("c10", typeof(char));
tblA.Rows.Add(1, 8500, "abc", 'A');
tblA.Rows.Add(2, 950, "cde", 'B');
tblA.Rows.Add(3, 150, "efg", 'C');
tblA.Rows.Add(4, 850, "ghi", 'D');
tblA.Rows.Add(5, 50, "ijk", 'E');
tblB.Rows.Add(1, 7500, "klm", 'F');
tblB.Rows.Add(2, 900, "mno", 'G');
tblB.Rows.Add(3, 150, "opq", 'H');
tblB.Rows.Add(4, 850, "qrs", 'I');
tblB.Rows.Add(5, 50, "stu", 'J');
tblC.Rows.Add(1, 7500, "uvw", 'K');
tblC.Rows.Add(2, 900, "wxy", 'L');
tblC.Rows.Add(3, 150, "yza", 'M');
tblC.Rows.Add(4, 850, "ABC", 'N');
tblC.Rows.Add(5, 50, "CDE", 'O');
After DataTable.Merge in MergeAll:
After some modifications to join the rows in MergeAll:
Update
Since this question arose in one of the comments, if the only relation between two tables is the index of a DataRow in the table and you want to merge both tables according to the index:
public static DataTable MergeTablesByIndex(DataTable t1, DataTable t2)
{
if (t1 == null || t2 == null) throw new ArgumentNullException("t1 or t2", "Both tables must not be null");
DataTable t3 = t1.Clone(); // first add columns from table1
foreach (DataColumn col in t2.Columns)
{
string newColumnName = col.ColumnName;
int colNum = 1;
while (t3.Columns.Contains(newColumnName))
{
newColumnName = string.Format("{0}_{1}", col.ColumnName, ++colNum);
}
t3.Columns.Add(newColumnName, col.DataType);
}
var mergedRows = t1.AsEnumerable().Zip(t2.AsEnumerable(),
(r1, r2) => r1.ItemArray.Concat(r2.ItemArray).ToArray());
foreach (object[] rowFields in mergedRows)
t3.Rows.Add(rowFields);
return t3;
}
Sample:
var dt1 = new DataTable();
dt1.Columns.Add("ID", typeof(int));
dt1.Columns.Add("Name", typeof(string));
dt1.Rows.Add(1, "Jon");
var dt2 = new DataTable();
dt2.Columns.Add("Country", typeof(string));
dt2.Rows.Add("US");
var dtMerged = MergeTablesByIndex(dt1, dt2);
The result table contains three columns ID,Name,Country and a single row: 1 Jon US

Related

Cannot find column '0' C#

I have the following piece of code.
dt3 = new System.Data.DataTable();
foreach (DataRow sourceRow in dt2.Rows) {
DataRow destRow = dt3.NewRow();
destRow[0] = sourceRow[2];
dt3.Rows.Add(destRow);
}
And it is generating following error on line destRow[0] = sourceRow[2];
System.IndexOutOfRangeException: Cannot find column 0.
What am I doing wrong? Is there any way around it without declaring columns beforehand?
Here we create a "source" DataTable with 3 columns, and select two of those columns for the new DataTable:
DataTable srcDataTable = new DataTable();
srcDataTable.Columns.Add("Column A", typeof(string));
srcDataTable.Columns.Add("Column B", typeof(int));
srcDataTable.Columns.Add("Column C", typeof(int));
DataTable dstDataTable = new DataTable();
var desiredColumns = new[] { "Column A", "Column C" };
foreach (DataColumn col in srcDataTable.Columns)
{
if (desiredColumns.Contains(col.ColumnName))
{
dstDataTable.Columns.Add(col.ColumnName, col.DataType, col.Expression);
}
}
Now you can simply loop through the source table and copy the row data as you need it. Example for copying the rows:
foreach (DataRow srcRow in srcDataTable.Rows)
{
var newRow = dstDataTable.NewRow();
foreach (var columnName in desiredColumns)
{
newRow[columnName] = srcRow[columnName];
}
dstDataTable.Rows.Add(newRow);
}
Alternative approach using column numbers:
DataTable srcDataTable = new DataTable();
srcDataTable.Columns.Add("Column A", typeof(string));
srcDataTable.Columns.Add("Column B", typeof(int));
srcDataTable.Columns.Add("Column C", typeof(int));
DataTable dstDataTable = new DataTable();
var desiredColumns = new int[] { 0, 2 };
Dictionary<int, int> columnMap = new Dictionary<int, int>();
for (int colNum = 0; colNum < desiredColumns.Length; ++colNum)
{
columnMap[colNum] = desiredColumns[colNum];
dstDataTable.Columns.Add(srcDataTable.Columns[desiredColumns[colNum]].ColumnName, srcDataTable.Columns[desiredColumns[colNum]].DataType, srcDataTable.Columns[desiredColumns[colNum]].Expression);
}
foreach (DataRow srcRow in srcDataTable.Rows)
{
var newRow = dstDataTable.NewRow();
for (int colNum = 0; colNum < desiredColumns.Length; ++colNum)
{
newRow[colNum] = srcRow[columnMap[colNum]];
}
dstDataTable.Rows.Add(newRow);
}
This line dt3 = new System.Data.DataTable(); creates a new DataTable. But this table doesn't contain any columns yet. In fact, it's an empty table.
This line destRow[0] = sourceRow[2]; tries to set the value of the first column of your table. However, your table doesn't contain any columns yet. And this is what the error message is trying to tell you.
You have to create your column after creating the table. You can do it like this:
DataColumn idColumn = new DataColumn();
idColumn.DataType = System.Type.GetType("System.Int32");
idColumn.ColumnName = "id";
dt3.Columns.Add(idColumn);
Only after this, you will be able to put data into your first column.
Please take a look at this example in the Microsoft docs
What am I doing wrong?
Table dt3 has no column 0, because you haven't added one
Is there any way around it without declaring columns beforehand?
Even though you don't necessarily know the column name or type of dt2's column 2 at compile time, you seem to know that you definitely want column 2 from dt2 to be column 0 of dt3, so make sure you add a column to dt3 that is the same type (and giving it the same name seems reasonable too) as column 2 of dt2:
dt3 = new System.Data.DataTable();
dt3.Columns.Add(dt2.Columns[2].ColumnName, dt2.Columns[2].DataType);
foreach (DataRow sourceRow in dt2.Rows) {
DataRow destRow = dt3.NewRow();
destRow[0] = sourceRow[2];
dt3.Rows.Add(destRow);
}
You can try to use Clone() method to copy structure/columns from source table to destination table:
var dt3 = dt2.Clone();
From the docs.

how to add two datatables dt1=2 column ,dt2=3 column, into one datatable dt3=5 column

how to add two datatables?
(dt1=name,phone_number)
and
(dt2=address,cityname)
Result:
(dt3=name,phone_number,address,cityname)
Ruben, you will need some primary key in order to combine both tables unless you are 100% sure that both data tables have the same number of rows and they will always be stored in the correct order.
There is a similar question answered and the link is: Combining n DataTables into a Single DataTable
Here goes the content of the provided link:
"The table has repeating primary keys after the Merge because no primary-key was defined. So either specify the PK or try this method here which i've written from scratch(so it's not really tested):
public static DataTable MergeAll(this IList<DataTable> tables, String primaryKeyColumn)
{
if (!tables.Any())
throw new ArgumentException("Tables must not be empty", "tables");
if(primaryKeyColumn != null)
foreach(DataTable t in tables)
if(!t.Columns.Contains(primaryKeyColumn))
throw new ArgumentException("All tables must have the specified primarykey column " + primaryKeyColumn, "primaryKeyColumn");
if(tables.Count == 1)
return tables[0];
DataTable table = new DataTable("TblUnion");
table.BeginLoadData(); // Turns off notifications, index maintenance, and constraints while loading data
foreach (DataTable t in tables)
{
table.Merge(t); // same as table.Merge(t, false, MissingSchemaAction.Add);
}
table.EndLoadData();
if (primaryKeyColumn != null)
{
// since we might have no real primary keys defined, the rows now might have repeating fields
// so now we're going to "join" these rows ...
var pkGroups = table.AsEnumerable()
.GroupBy(r => r[primaryKeyColumn]);
var dupGroups = pkGroups.Where(g => g.Count() > 1);
foreach (var grpDup in dupGroups)
{
// use first row and modify it
DataRow firstRow = grpDup.First();
foreach (DataColumn c in table.Columns)
{
if (firstRow.IsNull(c))
{
DataRow firstNotNullRow = grpDup.Skip(1).FirstOrDefault(r => !r.IsNull(c));
if (firstNotNullRow != null)
firstRow[c] = firstNotNullRow[c];
}
}
// remove all but first row
var rowsToRemove = grpDup.Skip(1);
foreach(DataRow rowToRemove in rowsToRemove)
table.Rows.Remove(rowToRemove);
}
}
return table;
}
You can call it in this way:
var tables = new[] { tblA, tblB, tblC };
DataTable TblUnion = tables.MergeAll("c1");
Used this sample data:
var tblA = new DataTable();
tblA.Columns.Add("c1", typeof(int));
tblA.Columns.Add("c2", typeof(int));
tblA.Columns.Add("c3", typeof(string));
tblA.Columns.Add("c4", typeof(char));
var tblB = new DataTable();
tblB.Columns.Add("c1", typeof(int));
tblB.Columns.Add("c5", typeof(int));
tblB.Columns.Add("c6", typeof(string));
tblB.Columns.Add("c7", typeof(char));
var tblC = new DataTable();
tblC.Columns.Add("c1", typeof(int));
tblC.Columns.Add("c8", typeof(int));
tblC.Columns.Add("c9", typeof(string));
tblC.Columns.Add("c10", typeof(char));
tblA.Rows.Add(1, 8500, "abc", 'A');
tblA.Rows.Add(2, 950, "cde", 'B');
tblA.Rows.Add(3, 150, "efg", 'C');
tblA.Rows.Add(4, 850, "ghi", 'D');
tblA.Rows.Add(5, 50, "ijk", 'E');
tblB.Rows.Add(1, 7500, "klm", 'F');
tblB.Rows.Add(2, 900, "mno", 'G');
tblB.Rows.Add(3, 150, "opq", 'H');
tblB.Rows.Add(4, 850, "qrs", 'I');
tblB.Rows.Add(5, 50, "stu", 'J');
tblC.Rows.Add(1, 7500, "uvw", 'K');
tblC.Rows.Add(2, 900, "wxy", 'L');
tblC.Rows.Add(3, 150, "yza", 'M');
tblC.Rows.Add(4, 850, "ABC", 'N');
tblC.Rows.Add(5, 50, "CDE", 'O');
After DataTable.Merge in MergeAll:
After some modifications to join the rows in MergeAll:
Update
Since this question arose in one of the comments, if the only relation between two tables is the index of a DataRow in the table and you want to merge both tables according to the index:
public static DataTable MergeTablesByIndex(DataTable t1, DataTable t2)
{
if (t1 == null || t2 == null) throw new ArgumentNullException("t1 or t2", "Both tables must not be null");
DataTable t3 = t1.Clone(); // first add columns from table1
foreach (DataColumn col in t2.Columns)
{
string newColumnName = col.ColumnName;
int colNum = 1;
while (t3.Columns.Contains(newColumnName))
{
newColumnName = string.Format("{0}_{1}", col.ColumnName, ++colNum);
}
t3.Columns.Add(newColumnName, col.DataType);
}
var mergedRows = t1.AsEnumerable().Zip(t2.AsEnumerable(),
(r1, r2) => r1.ItemArray.Concat(r2.ItemArray).ToArray());
foreach (object[] rowFields in mergedRows)
t3.Rows.Add(rowFields);
return t3;
}
Sample:
var dt1 = new DataTable();
dt1.Columns.Add("ID", typeof(int));
dt1.Columns.Add("Name", typeof(string));
dt1.Rows.Add(1, "Jon");
var dt2 = new DataTable();
dt2.Columns.Add("Country", typeof(string));
dt2.Rows.Add("US");
var dtMerged = MergeTablesByIndex(dt1, dt2);
The result table contains three columns ID,Name,Country and a single row: 1 Jon US"
Source: Combining n DataTables into a Single DataTable
Last Access: 04-August-2016
First you can filter right column in to separate data datable
DataTable filterDatatable1= new DataTable();
DataTable filterDatatable2= new DataTable();
DataTable filterDatatable1= OriginaDatatable1.DefaultView.ToTable(false, "ColumnName1", "ColimnName2");
DataTable filterDatatable1= OriginaDatatable1.DefaultView.ToTable(false, "ColumnName1", "ColimnName2", "ColimnName3");
Then you can use merge function
DataTable dtAll = new DataTable();
dtAll.Merge(filterDatatable1);
dtAll.Merge(filterDatatable2);

compare and merge the datatable in C# [duplicate]

Let T1 and T2 are DataTables with following fields
T1(CustID, ColX, ColY)
T2(CustID, ColZ)
I need the joint table
TJ (CustID, ColX, ColY, ColZ)
How this can be done in C# code in a simple way? Thanks.
If you are allowed to use LINQ, take a look at the following example. It creates two DataTables with integer columns, fills them with some records, join them using LINQ query and outputs them to Console.
DataTable dt1 = new DataTable();
dt1.Columns.Add("CustID", typeof(int));
dt1.Columns.Add("ColX", typeof(int));
dt1.Columns.Add("ColY", typeof(int));
DataTable dt2 = new DataTable();
dt2.Columns.Add("CustID", typeof(int));
dt2.Columns.Add("ColZ", typeof(int));
for (int i = 1; i <= 5; i++)
{
DataRow row = dt1.NewRow();
row["CustID"] = i;
row["ColX"] = 10 + i;
row["ColY"] = 20 + i;
dt1.Rows.Add(row);
row = dt2.NewRow();
row["CustID"] = i;
row["ColZ"] = 30 + i;
dt2.Rows.Add(row);
}
var results = from table1 in dt1.AsEnumerable()
join table2 in dt2.AsEnumerable() on (int)table1["CustID"] equals (int)table2["CustID"]
select new
{
CustID = (int)table1["CustID"],
ColX = (int)table1["ColX"],
ColY = (int)table1["ColY"],
ColZ = (int)table2["ColZ"]
};
foreach (var item in results)
{
Console.WriteLine(String.Format("ID = {0}, ColX = {1}, ColY = {2}, ColZ = {3}", item.CustID, item.ColX, item.ColY, item.ColZ));
}
Console.ReadLine();
// Output:
// ID = 1, ColX = 11, ColY = 21, ColZ = 31
// ID = 2, ColX = 12, ColY = 22, ColZ = 32
// ID = 3, ColX = 13, ColY = 23, ColZ = 33
// ID = 4, ColX = 14, ColY = 24, ColZ = 34
// ID = 5, ColX = 15, ColY = 25, ColZ = 35
I wanted a function that would join tables without requiring you to define the columns using an anonymous type selector, but had a hard time finding any. I ended up having to make my own. Hopefully this will help anyone in the future who searches for this:
private DataTable JoinDataTables(DataTable t1, DataTable t2, params Func<DataRow, DataRow, bool>[] joinOn)
{
DataTable result = new DataTable();
foreach (DataColumn col in t1.Columns)
{
if (result.Columns[col.ColumnName] == null)
result.Columns.Add(col.ColumnName, col.DataType);
}
foreach (DataColumn col in t2.Columns)
{
if (result.Columns[col.ColumnName] == null)
result.Columns.Add(col.ColumnName, col.DataType);
}
foreach (DataRow row1 in t1.Rows)
{
var joinRows = t2.AsEnumerable().Where(row2 =>
{
foreach (var parameter in joinOn)
{
if (!parameter(row1, row2)) return false;
}
return true;
});
foreach (DataRow fromRow in joinRows)
{
DataRow insertRow = result.NewRow();
foreach (DataColumn col1 in t1.Columns)
{
insertRow[col1.ColumnName] = row1[col1.ColumnName];
}
foreach (DataColumn col2 in t2.Columns)
{
insertRow[col2.ColumnName] = fromRow[col2.ColumnName];
}
result.Rows.Add(insertRow);
}
}
return result;
}
An example of how you might use this:
var test = JoinDataTables(transactionInfo, transactionItems,
(row1, row2) =>
row1.Field<int>("TransactionID") == row2.Field<int>("TransactionID"));
One caveat: This is certainly not optimized, so be mindful when getting to row counts above 20k. If you know that one table will be larger than the other, try to put the smaller one first and the larger one second.
This is my code. Not perfect, but working good. I hope it helps somebody:
static System.Data.DataTable DtTbl (System.Data.DataTable[] dtToJoin)
{
System.Data.DataTable dtJoined = new System.Data.DataTable();
foreach (System.Data.DataColumn dc in dtToJoin[0].Columns)
dtJoined.Columns.Add(dc.ColumnName);
foreach (System.Data.DataTable dt in dtToJoin)
foreach (System.Data.DataRow dr1 in dt.Rows)
{
System.Data.DataRow dr = dtJoined.NewRow();
foreach (System.Data.DataColumn dc in dtToJoin[0].Columns)
dr[dc.ColumnName] = dr1[dc.ColumnName];
dtJoined.Rows.Add(dr);
}
return dtJoined;
}
this function will join 2 tables with a known join field, but this cannot allow 2 fields with the same name on both tables except the join field, a simple modification would be to save a dictionary with a counter and just add number to the same name filds.
public static DataTable JoinDataTable(DataTable dataTable1, DataTable dataTable2, string joinField)
{
var dt = new DataTable();
var joinTable = from t1 in dataTable1.AsEnumerable()
join t2 in dataTable2.AsEnumerable()
on t1[joinField] equals t2[joinField]
select new { t1, t2 };
foreach (DataColumn col in dataTable1.Columns)
dt.Columns.Add(col.ColumnName, typeof(string));
dt.Columns.Remove(joinField);
foreach (DataColumn col in dataTable2.Columns)
dt.Columns.Add(col.ColumnName, typeof(string));
foreach (var row in joinTable)
{
var newRow = dt.NewRow();
newRow.ItemArray = row.t1.ItemArray.Union(row.t2.ItemArray).ToArray();
dt.Rows.Add(newRow);
}
return dt;
}
I tried to do this in next way
public static DataTable JoinTwoTables(DataTable innerTable, DataTable outerTable)
{
DataTable resultTable = new DataTable();
var innerTableColumns = new List<string>();
foreach (DataColumn column in innerTable.Columns)
{
innerTableColumns.Add(column.ColumnName);
resultTable.Columns.Add(column.ColumnName);
}
var outerTableColumns = new List<string>();
foreach (DataColumn column in outerTable.Columns)
{
if (!innerTableColumns.Contains(column.ColumnName))
{
outerTableColumns.Add(column.ColumnName);
resultTable.Columns.Add(column.ColumnName);
}
}
for (int i = 0; i < innerTable.Rows.Count; i++)
{
var row = resultTable.NewRow();
innerTableColumns.ForEach(x =>
{
row[x] = innerTable.Rows[i][x];
});
outerTableColumns.ForEach(x =>
{
row[x] = outerTable.Rows[i][x];
});
resultTable.Rows.Add(row);
}
return resultTable;
}
Note that if you have a DataSet, you will need to steal the table from the Dataset with dataSet.Table[0]

Return datatable/column values from two data table comparison

My Datatable 1 (dtOutput) Format (termid,faultid,faultdesc,faulttime,devicetype)
My Datatable 2 (dtOpenEvent) Format (termid,faultid)
I want to retrieve those values which are present in Datatable 2 but not in Datatable 1...based on two columns (termid,faultid) no table have primary keys.
I Searched on net and find code which return diff between two data table...
Now how can i retrieve column values from it ? either in another data table or in string variable
Code :-
DataTable dtOpenEvent;
dtOpenEvent = Generix.getOpenEvents(ref Connection);
DataTable dtOutput;
dtOutput = Generix.getFeedData(ref Connection);
var matched = from table1 in dtOpenEvent.AsEnumerable()
join table2 in dtOutput.AsEnumerable() on table1.Field<string>("ATM") equals table2.Field<string>("termid")
where table1.Field<int>("Event") == table2.Field<int>("faultid")
select table1;
var missing = from table1 in dtOpenEvent.AsEnumerable()
where !matched.Contains(table1)
select table1;
you can remove all of the columns in dt1 and then do except.
like this:
var diff =dt2.AsEnumerable().Except(dt1.AsEnumerable(), DataRowComparer.Default);
full example:
DataTable dt1 = new DataTable();
DataTable dt2 = new DataTable();
dt1.Columns.Add("termid", typeof(Int32));
dt1.Columns.Add("faultid", typeof(Int32));
dt1.Columns.Add("faultdesc");
dt2.Columns.Add("termid", typeof(Int32));
dt2.Columns.Add("faultid", typeof(Int32));
dt1.Rows.Add(1,2,"desc");
dt1.Rows.Add(3, 4, "desc");
dt1.Rows.Add(5, 6, "desc");
dt2.Rows.Add(1, 2);
dt2.Rows.Add(3, 4);
dt2.Rows.Add(7, 8);
dt1.Columns.Remove("faultdesc");
var diff =dt2.AsEnumerable().Except(dt1.AsEnumerable(), DataRowComparer.Default);
foreach (var row in diff)
{
Console.WriteLine(row["termid"] + " " + row["faultid"]); //prints 7 8
}
or instead of removing columns you can select them through linq or dataview like this:
var view = new DataView(dt1);
DataTable dt3 = view.ToTable(true, "termid", "faultid");
modified example:
DataTable dt1 = new DataTable();
DataTable dt2 = new DataTable();
dt1.Columns.Add("termid", typeof(Int32));
dt1.Columns.Add("faultid", typeof(Int32));
dt1.Columns.Add("faultdesc");
dt2.Columns.Add("termid", typeof(Int32));
dt2.Columns.Add("faultid", typeof(Int32));
dt1.Rows.Add(1,2,"desc");
dt1.Rows.Add(3, 4, "desc");
dt1.Rows.Add(5, 6, "desc");
dt2.Rows.Add(1, 2);
dt2.Rows.Add(3, 4);
dt2.Rows.Add(7, 8);
var view = new DataView(dt1);
DataTable dt3 = view.ToTable(true, "termid", "faultid");
var diff =dt2.AsEnumerable().Except(dt3.AsEnumerable(), DataRowComparer.Default);
foreach (var row in diff)
{
Console.WriteLine(row["termid"] + " " + row["faultid"]);
}
As you said : I want to retrieve those values which are present in Datatable 2
but not in Datatable 1...based on two columns `(termid,faultid)`
Translation according to the context of question : You have two tables dtOutput and dtOpenEvent. You want to get values of dtOutput in a third table such that no row of third table has same value with first two cells of any row of dtOpenEvent. Then here it is
DataTable dt3 = new DataTable();
dt3.Columns.Add("termid");
dt3.Columns.Add("faultid");
int nr = 0;
for (int i = 0; i < dtOutput.Rows.Count; i++)
{
bool found = false;
for (int j = 0; j < dtOpenEvent.Rows.Count; j++)
{
if (dtOutput.Rows[i][0] == dtOpenEvent.Rows[j][0]
&& dtOutput.Rows[i][1] == dtOpenEvent.Rows[j][1])
{
found = true;
break;
}
}
if (!found)
{
dt3.Rows.Add(dt3.NewRow());
dt3.Rows[nr][0] = dtOutput.Rows[i][0];
dt3.Rows[nr][1] = dtOutput.Rows[i][1];
nr++;
}
}

Inner join of DataTables in C#

Let T1 and T2 are DataTables with following fields
T1(CustID, ColX, ColY)
T2(CustID, ColZ)
I need the joint table
TJ (CustID, ColX, ColY, ColZ)
How this can be done in C# code in a simple way? Thanks.
If you are allowed to use LINQ, take a look at the following example. It creates two DataTables with integer columns, fills them with some records, join them using LINQ query and outputs them to Console.
DataTable dt1 = new DataTable();
dt1.Columns.Add("CustID", typeof(int));
dt1.Columns.Add("ColX", typeof(int));
dt1.Columns.Add("ColY", typeof(int));
DataTable dt2 = new DataTable();
dt2.Columns.Add("CustID", typeof(int));
dt2.Columns.Add("ColZ", typeof(int));
for (int i = 1; i <= 5; i++)
{
DataRow row = dt1.NewRow();
row["CustID"] = i;
row["ColX"] = 10 + i;
row["ColY"] = 20 + i;
dt1.Rows.Add(row);
row = dt2.NewRow();
row["CustID"] = i;
row["ColZ"] = 30 + i;
dt2.Rows.Add(row);
}
var results = from table1 in dt1.AsEnumerable()
join table2 in dt2.AsEnumerable() on (int)table1["CustID"] equals (int)table2["CustID"]
select new
{
CustID = (int)table1["CustID"],
ColX = (int)table1["ColX"],
ColY = (int)table1["ColY"],
ColZ = (int)table2["ColZ"]
};
foreach (var item in results)
{
Console.WriteLine(String.Format("ID = {0}, ColX = {1}, ColY = {2}, ColZ = {3}", item.CustID, item.ColX, item.ColY, item.ColZ));
}
Console.ReadLine();
// Output:
// ID = 1, ColX = 11, ColY = 21, ColZ = 31
// ID = 2, ColX = 12, ColY = 22, ColZ = 32
// ID = 3, ColX = 13, ColY = 23, ColZ = 33
// ID = 4, ColX = 14, ColY = 24, ColZ = 34
// ID = 5, ColX = 15, ColY = 25, ColZ = 35
I wanted a function that would join tables without requiring you to define the columns using an anonymous type selector, but had a hard time finding any. I ended up having to make my own. Hopefully this will help anyone in the future who searches for this:
private DataTable JoinDataTables(DataTable t1, DataTable t2, params Func<DataRow, DataRow, bool>[] joinOn)
{
DataTable result = new DataTable();
foreach (DataColumn col in t1.Columns)
{
if (result.Columns[col.ColumnName] == null)
result.Columns.Add(col.ColumnName, col.DataType);
}
foreach (DataColumn col in t2.Columns)
{
if (result.Columns[col.ColumnName] == null)
result.Columns.Add(col.ColumnName, col.DataType);
}
foreach (DataRow row1 in t1.Rows)
{
var joinRows = t2.AsEnumerable().Where(row2 =>
{
foreach (var parameter in joinOn)
{
if (!parameter(row1, row2)) return false;
}
return true;
});
foreach (DataRow fromRow in joinRows)
{
DataRow insertRow = result.NewRow();
foreach (DataColumn col1 in t1.Columns)
{
insertRow[col1.ColumnName] = row1[col1.ColumnName];
}
foreach (DataColumn col2 in t2.Columns)
{
insertRow[col2.ColumnName] = fromRow[col2.ColumnName];
}
result.Rows.Add(insertRow);
}
}
return result;
}
An example of how you might use this:
var test = JoinDataTables(transactionInfo, transactionItems,
(row1, row2) =>
row1.Field<int>("TransactionID") == row2.Field<int>("TransactionID"));
One caveat: This is certainly not optimized, so be mindful when getting to row counts above 20k. If you know that one table will be larger than the other, try to put the smaller one first and the larger one second.
This is my code. Not perfect, but working good. I hope it helps somebody:
static System.Data.DataTable DtTbl (System.Data.DataTable[] dtToJoin)
{
System.Data.DataTable dtJoined = new System.Data.DataTable();
foreach (System.Data.DataColumn dc in dtToJoin[0].Columns)
dtJoined.Columns.Add(dc.ColumnName);
foreach (System.Data.DataTable dt in dtToJoin)
foreach (System.Data.DataRow dr1 in dt.Rows)
{
System.Data.DataRow dr = dtJoined.NewRow();
foreach (System.Data.DataColumn dc in dtToJoin[0].Columns)
dr[dc.ColumnName] = dr1[dc.ColumnName];
dtJoined.Rows.Add(dr);
}
return dtJoined;
}
this function will join 2 tables with a known join field, but this cannot allow 2 fields with the same name on both tables except the join field, a simple modification would be to save a dictionary with a counter and just add number to the same name filds.
public static DataTable JoinDataTable(DataTable dataTable1, DataTable dataTable2, string joinField)
{
var dt = new DataTable();
var joinTable = from t1 in dataTable1.AsEnumerable()
join t2 in dataTable2.AsEnumerable()
on t1[joinField] equals t2[joinField]
select new { t1, t2 };
foreach (DataColumn col in dataTable1.Columns)
dt.Columns.Add(col.ColumnName, typeof(string));
dt.Columns.Remove(joinField);
foreach (DataColumn col in dataTable2.Columns)
dt.Columns.Add(col.ColumnName, typeof(string));
foreach (var row in joinTable)
{
var newRow = dt.NewRow();
newRow.ItemArray = row.t1.ItemArray.Union(row.t2.ItemArray).ToArray();
dt.Rows.Add(newRow);
}
return dt;
}
I tried to do this in next way
public static DataTable JoinTwoTables(DataTable innerTable, DataTable outerTable)
{
DataTable resultTable = new DataTable();
var innerTableColumns = new List<string>();
foreach (DataColumn column in innerTable.Columns)
{
innerTableColumns.Add(column.ColumnName);
resultTable.Columns.Add(column.ColumnName);
}
var outerTableColumns = new List<string>();
foreach (DataColumn column in outerTable.Columns)
{
if (!innerTableColumns.Contains(column.ColumnName))
{
outerTableColumns.Add(column.ColumnName);
resultTable.Columns.Add(column.ColumnName);
}
}
for (int i = 0; i < innerTable.Rows.Count; i++)
{
var row = resultTable.NewRow();
innerTableColumns.ForEach(x =>
{
row[x] = innerTable.Rows[i][x];
});
outerTableColumns.ForEach(x =>
{
row[x] = outerTable.Rows[i][x];
});
resultTable.Rows.Add(row);
}
return resultTable;
}
Note that if you have a DataSet, you will need to steal the table from the Dataset with dataSet.Table[0]

Categories

Resources