Linq query on large datatable - c#

I have a 2 datatables which I have created a join. I can then copy this back to a new datatable. This is fine and works without issue. However, if I have a datatable with a larger number of columns, say 30 with various datatypes, how can I copy them all to a new datatable without specifying each column, i.e. similar to a SQL query ?
Thanks.
var results = from table1 in dt.AsEnumerable()
join table2 in names.AsEnumerable()
on table1.Field<int>("Id") equals table2.Field<int>("Id")
select new
{
C1 = table1.Field<int>("Id"),
C2 = table1.Field<int>("Col1"),
C3 = table1.Field<int>("Col2"),
C4 = table2.Field<String>("Name")
};
DataTable sout = dt.Clone();
sout.Columns.Add("Name", typeof(string));
foreach (var v in results)
{
sout.Rows.Add(v.C1,v.C2,v.C3,v.C4);
};

First set primary keys for your tables.
dt.PrimaryKey = new[] {dt.Columns["Id"]};
names.PrimaryKey = new[] { names.Columns["Id"] };
Then, if you don't mind dt changing you can merge tables with the code,
dt.Merge(names,false, MissingSchemaAction.AddWithKey);
If you mind clone dt and do the same.

Related

How to: Join 2 DataTables with same Row Count

I've got these two DataTables:
Table1:
Columns->"Timestamp1,Result1"
Row[0]->"someTime,someResult"
Table2:
Columns->"Timestamp2,Result2"
Row[0]->"someotherTime", "someotherResult"
Now I want to get a result table or string[] or string like this:
Table Result:
Columns->"Timestamp1,Result1,Timestamp2,Result2"
Row[0]->"someTime,someResult,someotherTime,someotherResult"
Is there a simple Way of doing this? Even if the tables got more rows then one?
I've allready got some solution for joining the Columns to a string:
StringBuilder sb = new StringBuilder();
foreach (DataTable dt in data_set.Tables)
{
sb.Append(string.Join(",", dt.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToArray()));
sb.Append(",");
}
Output with the example tables will be a String like this >> "Timestamp1, Result1,Timestamp2,Result2"
So using linq could be possible, but unfortunately I dont get it...
Could someone help me please?
Best regards
Edit:
Both tables should have the same count of rows! Merge will not work, because it will just not join one rows from two different tables into a new single one. Merge will just output 2 Rows in the new table.
from your both tables, get the below result table :
var resultTable = from t1 in dataTable1.AsEnumerable()
join t2 in dataTable2.AsEnumerable()
on t1.Field<int>("ID") equals t2.Field<int>("ID")
select new { t1, t2 };
then after apply your logic on resultTable for getting your result
Ok I solved it on my own... this code looks for every column in dt2 and if it's not existing in dt1, it will start the copy process.
foreach (DataColumn dc in data_table2.Columns)
{
//Only new Columns
if (!data_table1.Columns.Contains(dc.ColumnName))
{
//Add all new Columns to dt1
data_table1.Columns.Add(dc.ColumnName, dc.DataType);
//interate over all rows
foreach (DataRow dr in data_table2.Rows)
{
//Copy single value from dt2 in dt1
data_table1.Rows[dr.Table.Rows.IndexOf(dr)][data_table1.Columns.IndexOf(dc.ColumnName)] = dr[dc.ColumnName];
}
}
}

How to fix "The source contains no DataRows"?

Here i want to find the Matched Records From Two data tables. the code is
public DataTable textfiltering(DataTable dtfff, DataTable dtff)
{
DataTable ds = (DataTable)Session["maintxt"];
DataTable dts = (DataTable)Session["sectxt"];
dtfff = ds;
dtff = dts;
DataTable dtMerged = (from a in dtfff.AsEnumerable()
join b in dtff.AsEnumerable()
on a["contacts"].ToString() equals b["contacts"].ToString()
into g
where g.Count()>0
select a).CopyToDataTable();
return dtMerged;
}
it gives "The source contains no DataRows" when Data tables does not contain Matched Records...
How to rectify it..pls give your suggistions
Two ways:
either check if it contains rows with Enumerable.Any before you call CopyToDataTable
use dtfff.Clone to create an empty DataTable with the same schema as the source table and use a loop to fill it from the LINQ query.
First approach:
var rows = from a in dtfff.AsEnumerable()
join b in dtff.AsEnumerable()
on a["contacts"].ToString() equals b["contacts"].ToString()
into g
where g.Count() > 0
select a;
DataTable merged;
if (rows.Any())
merged = rows.CopyToDataTable();
else
merged = dtfff.Clone();
return merged;
Second approach:
DataTable merged = dtfff.Clone();
foreach (DataRow sourceRow in rows)
{
merged.ImportRow(sourceRow); // or add all fields manually
}
return merged;
I prefer the second approach since it only needs to execute the query once.

Linq on DataTable: select specific column into datatable, not whole table

I'm running a LINQ query on a datatable in c#.
I want to select specific columns rather than the whole row and enter the result into a datatable. How can i do that??
My Code:
public DataTable getConversions(string c_to, string p_to)
{
var query = from r in matrix.AsEnumerable()
where r.Field<string>("c_to") == c_to &&
r.Field<string>("p_to") == p_to
select r;
DataTable conversions = query.CopyToDataTable();
If you already know beforehand how many columns your new DataTable would have, you can do something like this:
DataTable matrix = ... // get matrix values from db
DataTable newDataTable = new DataTable();
newDataTable.Columns.Add("c_to", typeof(string));
newDataTable.Columns.Add("p_to", typeof(string));
var query = from r in matrix.AsEnumerable()
where r.Field<string>("c_to") == "foo" &&
r.Field<string>("p_to") == "bar"
let objectArray = new object[]
{
r.Field<string>("c_to"), r.Field<string>("p_to")
}
select objectArray;
foreach (var array in query)
{
newDataTable.Rows.Add(array);
}
Try Access DataTable easiest way which can help you for getting perfect idea for accessing DataTable, DataSet using Linq...
Consider following example, suppose we have DataTable like below.
DataTable ObjDt = new DataTable("List");
ObjDt.Columns.Add("WorkName", typeof(string));
ObjDt.Columns.Add("Price", typeof(decimal));
ObjDt.Columns.Add("Area", typeof(string));
ObjDt.Columns.Add("Quantity",typeof(int));
ObjDt.Columns.Add("Breath",typeof(decimal));
ObjDt.Columns.Add("Length",typeof(decimal));
Here above is the code for DatTable, here we assume that there are some data are available in this DataTable, and we have to bind Grid view of particular by processing some data as shown below.
Area | Quantity | Breath | Length | Price = Quantity * breath *Length
Than we have to fire following query which will give us exact result as we want.
var data = ObjDt.AsEnumerable().Select
(r => new
{
Area = r.Field<string>("Area"),
Que = r.Field<int>("Quantity"),
Breath = r.Field<decimal>("Breath"),
Length = r.Field<decimal>("Length"),
totLen = r.Field<int>("Quantity") * (r.Field<decimal>("Breath") * r.Field<decimal>("Length"))
}).ToList();
We just have to assign this data variable as Data Source.
By using this simple Linq query we can get all our accepts, and also we can perform all other LINQ queries with this…
Here I get only three specific columns from mainDataTable and use the filter
DataTable checkedParams = mainDataTable.Select("checked = true").CopyToDataTable()
.DefaultView.ToTable(false, "lagerID", "reservePeriod", "discount");
LINQ is very effective and easy to use on Lists rather than DataTable. I can see the above answers have a loop(for, foreach), which I will not prefer.
So the best thing to select a perticular column from a DataTable is just use a DataView to filter the column and use it as you want.
Find it here how to do this.
DataView dtView = new DataView(dtYourDataTable);
DataTable dtTableWithOneColumn= dtView .ToTable(true, "ColumnA");
Now the DataTable dtTableWithOneColumn contains only one column(ColumnA).
Your select statement is returning a sequence of anonymous type , not a sequence of DataRows. CopyToDataTable() is only available on IEnumerable<T> where T is or derives from DataRow. You can select r the row object to call CopyToDataTable on it.
var query = from r in matrix.AsEnumerable()
where r.Field<string>("c_to") == c_to &&
r.Field<string>("p_to") == p_to
select r;
DataTable conversions = query.CopyToDataTable();
You can also implement CopyToDataTable Where the Generic Type T Is Not a DataRow.

How to query a DataTable in memory to fill another data table

I am trying to update a Microsoft report. What it does is write out how many clients where excluded from a conversion process and for what reason. Currently the program writes all of the deleted clients back to the server then queries it back to fill a specialty table with the results.
Here is the current query:
SELECT DeletedClients.Reason,
COUNT(DeletedClients.Reason) AS Number,
CAST(CAST(COUNT(DeletedClients.Reason) AS float)
/ CAST(t.Total AS float)
* 100 AS numeric(4, 1)) AS percentage
FROM DeletedClients CROSS JOIN
(SELECT COUNT(*) AS Total
FROM DeletedClients AS DeletedClients_1
WHERE (ClinicID = #ClinicID)) AS t
WHERE (DeletedClients.ClinicID = #ClinicID)
AND (DeletedClients.TotalsIdent = #ident)
GROUP BY DeletedClients.Reason, t.Total
ORDER BY Number DESC
What I would like to do is not write DeletedClients to the server as it already exists in memory in my program as a DataTable and it is just slowing down the report and filling the database with information we do not need to save.
My main question is this, Either :
How do I query a data table to make a new in memory data table that has the same results as if I wrote out the the SQL server and read it back in with the query above?
OR
How in Microsoft Reports do you do a group by clause for items in a Tablix to turn =Fields!Reason.Value =Fields!Number.Value =Fields!percentage.Value into something similar to the returned result from the query above?
You can use DataTable.Select to query the DataTable.
DataTable table = GetDataTableResults();
DataTable results = table.Select("SomeIntColumn > 0").CopyToDataTable();
Or for more complex queries, you can use LINQ to query the DataTable:
DataTable dt = GetDataTableResults();
var results = from row in dt.AsEnumerable()
group row by new { SomeIDColumn = row.Field<int>("SomeIDColumn") } into rowgroup
select new
{
SomeID = rowgroup.Key.SomeIDColumn,
SomeTotal = rowgroup.Sum(r => r.Field<decimal>("SomeDecimalColumn"))
};
DataTable queryResults = new DataTable();
foreach (var result in query)
queryResults.Rows.Add(new object[] { result.SomeID, result.SomeTotal });
There are two ways that I can think of to query the data table. Below is an example using both ways.
using System;
using System.Data;
namespace WindowsFormsApplication1
{
static class Program
{
[STAThread]
static void Main()
{
var deletedClients = GetDataTable();
// Using linq to create the new DataTable.
var example1 = deletedClients.AsEnumerable()
.Where(x => x.Field<int>("ClinicId") == 1)
.CopyToDataTable();
// Using the DefaultView RowFilter to create a new DataTable.
deletedClients.DefaultView.RowFilter = "ClinicId = 1";
var rowFilterExample = deletedClients.DefaultView.ToTable();
}
static DataTable GetDataTable()
{
var dataTable = new DataTable();
// Assumes ClinicId is an int...
dataTable.Columns.Add("ClinicId", typeof(int));
dataTable.Columns.Add("Reason");
dataTable.Columns.Add("Number", typeof(int));
dataTable.Columns.Add("Percentage", typeof(float));
for (int counter = 0; counter < 10; counter++)
{
dataTable.Rows.Add(counter, "Reason" + counter, counter, counter);
}
return dataTable;
}
}
}

How to join DataTables?

I have two DataTables. I have some data that was been retrieved from an XML file on the internet into a DataTable called rates. I also have a DataTable with a set of finanical trades that has been retrieved from a database called openTrades with code like this:
DataTable rates = DB.GetCurrentFxPrices("http://rates.fxcm.com/RatesXML");
DataTable openTrades = DB.GetOpenTrades();
I want to add a column to openTrades called rate and put in there the current rate from the rates DataTable joining on a column called symbol.
the rates DataTable has the following columns:
Bid, Ask, High, Low, Direction, Last, Symbol
The openTrades DataTable has the following relevant columns:
tradeId, symbol and the newly added rate column. I'm looking for the most efficient way to join this data together and have the results in the openTrades DataTable in the new rate column.
EDIT
I'm trying this code:
DBUtil DB = new DBUtil();
DataTable rates = DB.GetCurrentFxPrices("http://rates.fxcm.com/RatesXML");
DataTable openTrades = DB.GetOpenTrades();
openTrades.Columns.Add("Bid", typeof(decimal));
openTrades.Columns.Add("Ask", typeof(decimal));
var query = from DataRow tradeRow in openTrades.Rows
join DataRow rateRow in rates.Rows
on tradeRow.Field<string>("symbol") equals rateRow.Field<string>("Symbol")
select new
{
TradeRow = tradeRow,
//Bid = rateRow.Field<decimal>("Bid"),
//Ask = rateRow.Field<decimal>("Ask")
Rate = (rateRow.Field<decimal>("Bid") + rateRow.Field<decimal>("Ask"))/2
};
foreach (var item in query)
{
//item.TradeRow["Bid"] = item.Bid;
//item.TradeRow["Ask"] = item.Ask;
item.TradeRow["lastPrice"] = item.Rate;
}
But I get this error on the select:
System.InvalidCastException: Specified cast is not valid.
You can join your existing tables with the following query, and then iterate over the resulting sequence to update your Rate values in the trade table.
var query = from DataRow tradeRow in openTrades.Rows
join DataRow rateRow in rates.Rows
on tradeRow.Field<string>("Symbol") equals rateRow.Field<string>("Symbol")
select new
{
TradeRow = tradeRow,
Rate = rateRow.Field<decimal>("Rate") // use actual type
};
foreach (var item in query)
{
item.TradeRow["Rate"] = item.Rate;
}
Your openTrades table should reflect the changes, and you can continue to do whatever work you need.
Have you looked into using Linq to do this?
Something similar to the following should do what you need. The resulting anonymous type could be loaded into a DataTable if needed or just bound directly with a grid control.
using (var wc = new System.Net.WebClient()) {
var openTrades = new [] {
new {tradeId="000", symbol="EURUSD"},
new {tradeId="001", symbol="USDJPY"}
};
var resultData = XElement.Parse(wc.DownloadString("http://rates.fxcm.com/RatesXML"))
.Elements("Rate")
.Select(x=>new {
Symbol=x.Attribute("Symbol").Value,
Bid=x.Element("Bid").Value,
Ask=x.Element("Ask").Value,
High=x.Element("High").Value,
Low=x.Element("Low").Value,
Direction=x.Element("Direction").Value,
Last=x.Element("Last").Value
})
.ToList()
.Join(openTrades, x=>x.Symbol, x=>x.symbol, (rate,trades)=> new {trades.tradeId, rate.Symbol, rate.Ask, rate.Bid, rate.High, rate.Low, rate.Direction, rate.Last})
.ToList();
}
To make this work, you will need the following namespaces referenced:
System.Linq
System.Xml.Linq
Good luck!

Categories