简体   繁体   中英

c# datatable using compute to calculate an average on a range of rows

I am trying to use .Compute on a datatable whereby it only calculates an average on a set of rows. I have looked through the Microsoft docs and searched for a while and cannot find where a filter works against the datatable structure. Looking for something like: -

string connectStatement = "SELECT Price FROM PData WHERE Code = '" + this.ItemCode + "' ORDER BY TradeDate DESC OFFSET 0 ROWS FETCH NEXT 300 ROWS ONLY";
DataTable itemsDT = ConnectionManager.GetTable(connectStatement);
Records20 = (decimal)itemsDT.Compute("Avg(Price)", "Rows > 16 AND Rows < 23");

It works when using an empty filter but calculates on all of the data.

If this is not possible, I am considering using a loop and sum to calculate the average after.

EDIT: I am looking at sample of data at a point in time and averaging a range either side of this (eg being the 20th interval and rows 17 - 22)

The error message is $exception {"Cannot find column [Rows]."} - System.Data.EvaluateException

Sample data (limited to first 30 rows):

Price 6464.100, 6426.800, 6406.100, 6349.300, 6329.600, 6295.500, 6247.900, 6189.100, 6206.100, 6214.600, 6231.000, 6130.200, 6115.700, 6129.500, 6097.300, 6159.600, 6149.700, 6118.400, 6156.700, 6118.800, 6054.700, 6014.700, 6198.000, 6242.800, 6272.900, 6250.700, 6213.500, 6207.000, 6184.200, 6333.200

Here is my solution using a loop

decimal sumCalculation = 0;
for (int record = 17; record < 23; record++)
{
     sumCalculation += decimal.Parse(itemsDT.Rows[record]["Price"].ToString());
}
Records20 = sumCalculation / 6;

You won't be able to do this the way you're approaching it.

To use the .Compute method, you need to pass a column name in the filter expression - as it is a normal WHERE clause. At a T-SQL level you can utilize the ROW_NUMBER function to enumerate the returned rows in a new column - say Index - then apply the filter on that enumerated column. Eg,

string connectStatement = "SELECT ROW_NUMBER() OVER(ORDER BY TradeDate DESC) AS Index, Price FROM PData WHERE Code = '" + this.ItemCode + "' OFFSET 0 ROWS FETCH NEXT 300 ROWS ONLY";
DataTable itemsDT = ConnectionManager.GetTable(connectStatement);
Records20 = (decimal)itemsDT.Compute("Avg(Price)", "Index > 16 AND Index < 23");

Another approach would be to do this with the aid of LINQ and leave the T-SQL query as is:

string connect statement = "SELECT Price FROM PData WHERE Code = '" + this.ItemCode + "' ORDER BY TradeDate DESC OFFSET 0 ROWS FETCH NEXT 300 ROWS ONLY";
Records20 = itemsDT.AsEnumerable() // this will yeald a IEnumerable<DataRow>
    .Skip(16)
    .Take(7)
    .Select(dr => (decimal)dr["Price"])
    .Average()
;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM