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!
Related
I have a stored procedure that returns extra columns. I don't have control of the stored procedure. I would like to use the following form to generate my worksheet:
ws.Cells.LoadFromDataTable(dt, true, OfficeOpenXml.Table.TableStyles.Light8)
How can I output just the columns I want?
I tried to figure out a Linq query but the problem is that the column names are inaccessible so I can't specify which columns I would like.
SqlConnection cx = new SqlConnection(util.GetConnectionString());
SqlCommand cmd = new SqlCommand("StoredRept", cx);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter ta = new SqlDataAdapter();
ta.SelectCommand = cmd;
DataTable dt = new DataTable();
ta.Fill(dt);
FileInfo newFile = new FileInfo("c:\temp");
ExcelPackage epp = new ExcelPackage(newFile);
var ws = epp.Workbook.Worksheets.Add("WS");
// here is where I would like to copy certain columns off into another data table but I haven't been able to figure out how
ws.Cells.LoadFromDataTable(dt, true, OfficeOpenXml.Table.TableStyles.Light8);
Any help would be greatly appreciated.
If removing some columns from the table is the only problem, that is easy to resolve. Try something like this:
var dt = new DataTable();
dt.Columns.Add("First", typeof(string));
dt.Columns.Add("Second", typeof(string));
dt.Columns.Add("Third", typeof(string));
dt.Columns.Add("Fourth", typeof(string));
dt.Columns.Add("Fifth", typeof(string));
for (var i = 1; i < 6; i++)
{
dt.Rows.Add($"First {i}", $"Second {i}", $"Third {i}",$"Fourth {i}",$"Fifth {i}");
}
//dt.Dump();//If you have linqpad this is handy to dump table to output
//REMOVE THE COLUMNS.
dt.Columns.RemoveAt(1);
dt.Columns.RemoveAt(2);
//dt.Dump();//again in linqpad this dumps table with remaining 3 columns
You can use following method to find column by name and remove it:
var col=dt.Columns["Second"];
dt.Columns.Remove(col);
Here is linq query to get list with desired columns.
var lq = (from DataRow r in dt.Rows
select new { First = r[0], Second=r[1], Third=r["Fifth"]}
).ToList();
lq.Dump();
Note how you can use both column index or name to get value from row object.
Can you load your spreadsheet with IEnumerable<T> instead of DataTable? If so:
var someColumns = dt
.AsEnumerable()
.Select(row => new
{
When = row.Field<DateTime>("When"),
What = row.Field<string>("What"),
HowMany = row.Field<int>("ColumnNameInDatabase"),
});
If you need DataTable, you can convert IEnumerable to it via any method, like this or this.
BUT
Frankly, I answered your problem, but IMO you've a greater problem at hand, which is using those antiquated DataTable objects instead of IEnumerable<T>. If you consider, for example, Dapper library, you could do this:
util.GetConnectionString()
.Query("StoredRept", commandType: CommandType.StoredProcedure) // Extension method from Dapper
.Select(dyn => new
{
When = (DateTime)dyn.When,
What = (string)dyn.What,
HowMany = (int)dyn.ColumnNameInDatabase
})
I see you're using EPPlus library for Excel manipulation. It can load IEnumerable<T> data, you are not restricted to DataTable.
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.
I have a DataTable with a variable number of columns.
I want to create a LINQ query that returns the data from the columns that start with a 'B_'.
I have a query that returns the columns names that start with 'B_'. It is below:
var arrayNames = (from DataColumn x in stationTable.Columns
where x.ColumnName.Contains("B_")
select x.ColumnName).ToArray();
Now that I have the column names how to I create a query using this array to return the data in the columns?
Thanks
You can create a DataView that hides the columns not in your list - that way you get to keep any type information:
var arrayNames = (from DataColumn x in stationTable.Columns
where !x.ColumnName.Contains("B_") // note the reversal
select x.ColumnName).ToArray();
DataView dv = new DataView(stationTable);
foreach (string colName in arrayNames)
dv.Table.Columns[colName].ColumnMapping = MappingType.Hidden
There are a couple of ways to approach this.
If you don't care about grouping the items by column type, this query accomplishes that:
var query = from DataColumn col in stationTable.Columns
from DataRow row in stationTable.Rows
where col.ColumnName.StartsWith("B_")
select row[col.ColumnName];
However, to maintain the grouping, you could use a lookup as follows:
var query = (from DataColumn col in stationTable.Columns
from DataRow row in stationTable.Rows
where col.ColumnName.StartsWith("B_")
select new { Row = row[col.ColumnName], col.ColumnName })
.ToLookup(o => o.ColumnName, o => o.Row);
foreach (var group in query)
{
Console.WriteLine("ColumnName: {0}", group.Key);
foreach (var item in group)
{
Console.WriteLine(item);
}
}
The downside of either approach is you're ending up with an object. Retaining the results in a strongly typed manner would require some extra work given the dynamic nature of the question.
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.
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;
}
}
}