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]
I am not using Entity Framework.
There are 2 Datatables, each with different no. of columns. There is one common column - ItemId
If it was Union, I would have used 'Merge', but don't know how to do a Union All for these 2 DataTables
public static void Main()
{
//First DataTable
DataTable dt1 = new DataTable();
dt1.Columns.Add("ItemId");
dt1.Columns.Add("Name");
dt1.Columns.Add("Color");
DataRow dr = dt1.NewRow();
dr["ItemId"] = "1";
dr["Name"] = "Name1";
dr["Color"] = "Color1";
dt1.Rows.Add(dr);
dr = dt1.NewRow();
dr["ItemId"] = "2";
dr["Name"] = "Name2";
dr["Color"] = "Color2";
dt1.Rows.Add(dr);
//Second DataTable
DataTable dt2 = new DataTable();
dt2.Columns.Add("ItemId");
dt2.Columns.Add("Name");
dt2.Columns.Add("Price");
DataRow dr2 = dt2.NewRow();
dr2["ItemId"] = "1";
dr2["Name"] = "Name1";
dr2["Price"] = "100";
dt2.Rows.Add(dr2);
dr2 = dt2.NewRow();
dr2["ItemId"] = "2";
dr2["Name"] = "Name3";
dr2["Price"] = "200";
dt2.Rows.Add(dr2);
}
Expected Output
ItemId Name Color Price
1 Name1 Color1
2 Name2 Color2
1 Name1 100
2 Name3 200
You can use DataTable.Merge()
If you have primary key in both the table then it will perform merge on primary key else it will directly append all the records.
In youe case make ItemID as primary key.
using System.Linq.Expressions;
......
var result1 = from row1 in dt1.AsEnumerable()
select new { Name = row1.Field<String>("Name"), Color = row1.Field<String>("Color"), Price = "" };
var result2 = from row1 in dt2.AsEnumerable()
select new { Name = row1.Field<String>("Name"), Color = "", Price = row1.Field<String>("Price") };
var res = result1.Concat(result2);
foreach (var item in res)
Console.WriteLine("{0} - {1} - {2}", item.Name, item.Color, item.Price);
Even though this question is years old, for anyone looking for another way to do this:
public static DataTable MergeTables(DataTable dt1, DataTable dt2)
{
DataTable dt3 = dt1.Clone();
foreach (DataColumn col in dt2.Columns)
{
string strColumnName = col.ColumnName;
int intColNum = 1;
while (dt3.Columns.Contains(strColumnName))
{
strColumnName = string.Format("{0}_{1}", col.ColumnName, ++intColNum);
}
dt3.Columns.Add(strColumnName, col.DataType);
}
var Mergered = dt1.AsEnumerable().Zip(dt2.AsEnumerable(), (r1, r2) => r1.ItemArray.Concat(r2.ItemArray).ToArray());
foreach (object[] rowFields in Mergered)
dt3.Rows.Add(rowFields);
return dt3;
}
I have a program by VC2008 that send dataTable I sended it using the constractor I solve it help at losing data entered in dataview C# - to datagridview in another Form it work fine but I want to send only the new row I tried to copy using ImportRrow it to another datatable but it gives Form2 Blank.
and I send the data as datatable by method as following:
public DataTable showout2()
{
DataTable dtab = new DataTable();
DataColumn dc1 = new DataColumn("رقم المتسلسل");
DataColumn dc2 = new DataColumn("رقم الحساب");
DataColumn dc3 = new DataColumn("أسم الحساب");
DataColumn dc4 = new DataColumn("المالك");
DataColumn dc5 = new DataColumn("قيمة");
DataColumn dc6 = new DataColumn("نوع العملة");
DataColumn dc7 = new DataColumn("الدائن");
DataColumn dc8= new DataColumn("المدين");
DataColumn dc9= new DataColumn("تاريخ");
DataColumn dc10 = new DataColumn("تفاصيل");
dtab.Columns.Add(dc1);
dtab.Columns.Add(dc2);
dtab.Columns.Add(dc3);
dtab.Columns.Add(dc4);
dtab.Columns.Add(dc5);
dtab.Columns.Add(dc6);
dtab.Columns.Add(dc7);
dtab.Columns.Add(dc8);
dtab.Columns.Add(dc9);
dtab.Columns.Add(dc10);
DateTime date = new DateTime();
date = DateTime.Now;
// Create an array for the values.
object[] newRow = new object[10];
Set the values of the array.
string s = numb.Text;
newRow[0] = numb.Text;
newRow[1] = Account_numb.Text;
newRow[2] = Account_nam.Text;
newRow[3] = owner.Text;
newRow[4] = curency.Text;
newRow[5] = comboBox1.Text;
newRow[6] = Depet.Text;
newRow[7] = cridet.Text;
newRow[8] = date.ToString();
newRow[9] = note.Text;
declare DataRow and adding it to DataTable
DataRow row;
dtab.BeginLoadData();
// Add the new row to the rows collection.
row = dtab.LoadDataRow(newRow, true);
return dtab;
}
I transfer the data from Form as following
cashagree fm = cashagree(shouwout2());
I copy the datarow in constractor take datatable as prameter as following:
public Cashagree(DataTable d2)
{
dt.ImportRow(d2.Rows[0]);
}
here I assign the datasource and mange the secound datagridview
private void Cashagree_Load_1(object sender, EventArgs e)
{
try
{
for (int i = 0; i != dt.Rows.Count; i++){
label1.Text= dt.Rows[i].ToString();
if (string.IsNullOrEmpty(dt.Rows[i].ToString()))
{
MessageBox.Show("This is empty");
}
}
dataGridView.DataSource = dt;
dataGridView.Columns[3].Visible = false;
dataGridView.Columns[4].Visible = false;
dataGridView.Columns[5].Visible = false;
dataGridView.Columns[6].Visible = false;
dataGridView.Columns[7].Visible = false;
}
catch (Exception w) { MessageBox.Show("Error" + w.StackTrace); }
}
thank you for helping
i want to create datatable in which i want to add column names dynamically, my column names are coming from database, which is not fixed column name every time different its depend on user selection
i using sql server and c#.
Following code add column and row dynamically in the datatable:
DataTable dt = new DataTable();
var properties = typeof(model).GetProperties();
foreach (PropertyInfo p in properties)
{
dt.Columns.Add(p.Name, p.PropertyType);
}
foreach (var data in taskData)
{
var values = new object[properties.Length];
for (int i = 0; i < properties.Length; i++)
{
values[i] = properties[i].GetValue(data, null);
}
dt.Rows.Add(values);
}
Here is an option
var dt = new DataTable();
dt.Columns.Add("Name",typeof(string));
you may also try this one.
var dt = new DataTable();
DataColumn column = new DataColumn();
{
column.Caption = "Name";
column.ColumnName = "ColumnName";
column.DataType = typeof(String);
dt.Columns.Add(column);
}
For Performance improvement I want to convert datatable to datareader. I can not do that through query. So is there any other way to do so?
I know this is old, but the answers here seem to have missed the point of the OPs question.
DataTables have a method called CreateDataReader which will allow you to convert a DataTable to a DbDataReader object. In this case a DataTableReader.
DataTable table = new DataTable();
//Fill table with data
//table = YourGetDataMethod();
DataTableReader reader = table.CreateDataReader();
I should point out that this will not increase performance since you should be using one or the other.
Here are some more resources on the matter:
DataReader Vs DataTable
Is datareader quicker than dataset when populating a datatable?
For example
public DataTable ConvertDataReaderToDataTable(SqlDataReader dataReader)
{
DataTable datatable = new DataTable();
DataTable schemaTable = dataReader.GetSchemaTable();
try
{
foreach (DataRow myRow in schemaTable.Rows)
{
DataColumn myDataColumn = new DataColumn();
myDataColumn.DataType = myRow.GetType();
myDataColumn.ColumnName = myRow[0].ToString();
datatable.Columns.Add(myDataColumn);
}
while (dataReader.Read())
{
DataRow myDataRow = datatable.NewRow();
for (int i = 0; i < schemaTable.Rows.Count; i++)
{
myDataRow[i] = dataReader[i].ToString();
}
datatable.Rows.Add(myDataRow);
myDataRow = null;
}
schemaTable = null;
return datatable;
}
catch (Exception ex)
{
Error.Log(ex.ToString());
return datatable;
}
}
Use DataTable constructor,
DataTable table = new DataTable();
//Fill table with data
DataTableReader reader = new DataTableReader(table);
Good Look!
public DataTable GetTable(IDataReader _reader)
{
DataTable dataTable1 = _reader.GetSchemaTable();
DataTable dataTable2 = new DataTable();
string[] arrayList = new string[dataTable1.Rows.Count];
for (int i = 0; i < dataTable1.Rows.Count; i++)
{
DataColumn dataColumn = new DataColumn();
if (!dataTable2.Columns.Contains(dataTable1.Rows[i]["ColumnName "].ToString()))
{
dataColumn.ColumnName = dataTable1.Rows[i]["ColumnName "].ToString();
dataColumn.Unique = Convert.ToBoolean(dataTable1.Rows[i]["IsUnique "]);
dataColumn.AllowDBNull = Convert.ToBoolean(dataTable1.Rows[i]["AllowDBNull "]);
dataColumn.ReadOnly = Convert.ToBoolean(dataTable1.Rows[i]["IsReadOnly "]);
dataColumn.DataType = (Type)dataTable1.Rows[i]["DataType "];
arrayList[i] = dataColumn.ColumnName;
dataTable2.Columns.Add(dataColumn);
}
}
dataTable2.BeginLoadData();
while (_reader.Read())
{
DataRow dataRow = dataTable2.NewRow();
for (int j = 0; j < arrayList.Length; j++)
{
dataRow[arrayList[j]] = _reader[arrayList[j]];
}
dataTable2.Rows.Add(dataRow);
}
_reader.Close();
dataTable2.EndLoadData();
return dataTable2;
}