I have the following class:
internal class ModuleScrap
{
public System.DateTime ReadTime { get; set; }
public string ScrapReason { get; set; }
public Int16 NetScrap { get; set; }
}
I could use some help with a LINQ query that retrieves all rows between a range of dates (that's the easy part), groups on ScrapReason and determines the sum of NetScrap for each group. I don't have much experience with grouping in LINQ.
Something like this, I suspect:
var query = from scrap in scraps
where scrap.ReadTime >= minDate && scrap.ReadTime <= maxDate
group scrap by scrap.ScrapReason into g
select new { Reason = g.Key, Total = g.Sum(x => (int) x.NetScrap) };
Note that the int cast is because Sum isn't defined for Int16 values. An alternative is to change the grouping:
var query = from scrap in scraps
where scrap.ReadTime >= minDate && scrap.ReadTime <= maxDate
group (int) scrap.NetScrap by scrap.ScrapReason into g
select new { Reason = g.Key, Total = g.Sum() };
They'll give the same results - it's just a matter of which you prefer.
Related
I'm fetching Invoices from database and I want to return all invoices without grouping them!
I don't want to group them since If there are 100 invoices I want to return all of them 100, considering that I want to get Sum of Amount.
So it is perfectly fine to repeat same Total for multiple invoices if their sum is the same, so basically I want to calculate sum of Amount of each invoice item and group by CompanyId, PackageId, BankId, PayMethod only if it's possible?
-- Read code comments --
var result = await _dbContext.Invoices
.Where(p => p.CheckDate >= startDate && p.CheckDate <= endDate)
.Select(p => new DemoDto()
{
CompanyId = p.CompanyId,
Title = p.Title,
Price = p.Price
Total = p.Sum(p => p.Amount).ToString(), // Can I sum here and group by fields I mentioned above? without grouping all data set because I want to keep all 100 records if I received all 100 from database
})
.ToListAsync();
This query obliviously doesn't work because it says
Invoice does not contain definition for Sum and no accessible method..
DemoDto looks like this:
public class DemoDto
{
public string CompanyId {get;set;}
public string Title {get;set;}
public decimal Price {get;set;}
public decimal Amount {get;set;}
}
Invoice class looks like this:
public class Invoice
{
public string CompanyId { get; set; }
public int PackageId {get; set;}
public int BankId {get;set;}
public int PayMethod {get;set;}
public string Title { get; set; }
public decimal Price { get; set; }
public decimal Amount { get; set; }
}
what I'm missing here?
How can I achieve this?
Thanks guys
Cheers
Fetch all the invoices from the database:
var invoices = await _dbContext.Invoices
.Where(p => p.CheckDate >= startDate && p.CheckDate <= endDate)
.ToListAsync();
Group the in-memory results using Linq-To-Object:
var result = invoices?
.GroupBy(p => new { p.CompanyId, p.PackageId, p.BankId, p.PayMethod })
.SelectMany(x => x.Select(y =>
new DemoDto
{
CompanyId = y.CompanyId,
Title = y.Title,
Price = y.Price,
Total = x.Sum(z => z.Price)
}))
.ToList();
If you want to perform the grouping in the database for some reason, you should execute a raw SQL query or a stored procedure rather than relying on the ORM to generate some magic (and most probably inefficient) query for you.
I have this error in my LINQ statement, however i dont understand whats the problem with it or how can i solve it:
This is my LINQ:
int year = 2016;
int month = 11;
DateTime dateFrom = new DateTime(year, month, 1);
DateTime dateTo = dateFrom.AddMonths(1);
int daysInMonth = DateTime.DaysInMonth(year, month);
var data = db_pdv.Pdv.Where(x => x.Fecha >= dateFrom && x.Fecha < dateTo);
var model_pdv = data.GroupBy(x => new { Pdv = x.Clave_PDV, Nombre_Pdv = x.Nombre_Pdv, Turno = x.Turno, Nombre_Turno = x.Nombre_Turno, Pla_ID = x.Platillo, Nombre_Platillo = x.Nombre_Platillo, Precio = x.Precio })
.Select(x => new DishVM()
{
Clave_PDV = x.Key.Pdv,
Nombre_Pdv = x.Key.Nombre_Pdv,
Turno = x.Key.Turno,
Nombre_Turno = x.Key.Nombre_Turno,
Platillo = x.Key.Pla_ID,
Nombre_Platillo = x.Key.Nombre_Platillo,
Precio = x.Key.Precio,
Days = new List<int>(new int[daysInMonth]),
Data = x
}).ToList();
And this is my "DishVM" CLass
public class DishVM
{
public string Clave_PDV { get; set; }
public string Nombre_Pdv { get; set; }
public string Turno { get; set; }
public string Nombre_Turno { get; set; }
public int Platillo { get; set; }
public string Nombre_Platillo { get; set; }
[DisplayFormat(DataFormatString = "{0:C}")]
public decimal Precio { get; set; }
public List<int> Days { get; set; }
[Display(Name = "Quantity")]
public int TotalQuantity { get; set; }
[DisplayFormat(DataFormatString = "{0:C}")]
[Display(Name = "Total")]
public decimal TotalPrice { get; set; }
public IEnumerable<Pdv> Data { get; set; }
}
How can i solve this problem?
Thanks in advance
How can I solve this problem?
Start by knowing what the problem is.
In Entity Framework, any expression that operates on a DbSet, like db_pdv.Pdv, is translated into SQL. The whole expression. In your case, this "whole expression" is model_pdv, which is structured as db_pdv.Pdv.Where(...).GroupBy(...).Select(). The expression contains new List<int>(new int[daysInMonth]). You'll understand that it's impossible to translate this into SQL; how would a database engine know how to construct a .Net List<T> object?
So how to solve it?
You could build the list first and then build the expression:
...
var daysList = new List<int>(new int[daysInMonth]);
var data = db_pdv.Pdv.Where(...
...
Precio = x.Key.Precio,
Days = daysList,
Data = x
Now you've reduced the SQL translation task to converting primitive values (integers) into SQL. EF knows perfectly well how to do that. But the result is... funny. If you check the generated SQL you see that EF converts the list of integers into some sort of a SQL table. Which looks like ...
CROSS JOIN (SELECT
0 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
UNION ALL
SELECT
0 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable2]
UNION ALL
SELECT
0 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable3]
UNION ALL
...
... etcetera.
What happens here, basically, is build a list in c#, convert it into a SQL construct, let the database construct a result set, convert the result set into a list in c# -- complicated.
Another option is to run the whole statement in memory:
var data = db_pdv.Pdv.Where(x => x.Fecha >= dateFrom && x.Fecha < dateTo)
.AsEnumerable();
var model_pdv = ...
Normally, I don't advocate this approach, for reasons explained here. But in this case it would be OK, because in the end you're going to use all data (Data = x), so you won't fetch more than you need from the database.
A more profound solution would be to remove the list from the view model altogether. Redundancy is the mother of inconsistency. Why should all model instances need the same list of integers? You should be able to build the list only once where you need it to present a UI.
Let's say I have the following data in a database.
class Data
{
public int Category { get; set; }
public int ValueA { get; set; }
public int ValueB { get; set; }
}
How can I write a LINQ query to get the sum of ValueA and also the sum of ValueB for all rows with Category == 1?
I know I could load all the data and then use Sum on the loaded data but would prefer to total them in the database.
I know I can use group by but I'm not grouping by anything. I just want these two totals from the data.
If you are using EF, you can try this:
var result= context.Data.Where(d=>d.Category == 1)
.GroupBy(d=>d.Category)
.Select(g=>new {
SumA=g.Sum(d=>d.ValueA),
SumB=g.Sum(d=>d.ValueB)
}
);
You can group by a constant
var result = from d in context.Data
where d.Category == 1
group d by 1 into g
select
{
ASum = g.Sum(d => d.ValueA),
BSum = g.Sum(d => d.ValueB)
};
Or as octavioccl pointed out you can also group by Category since it will be a constant value because of the where clause. But using a constant is how you can achieve what you want in the general case.
I want create a poll. i have a table for questions(Fields: ID,Question) and another for answers(Fields: ID,QuestionID,Answer). There is a table for the results(fields: QuestionID,AnswerID,UserID).i want to show the Percent of responding to any item in datagridview.
for example when i enter question id, datagridview show:
Choose question ID:1
Option...........Percent
1 ------------------------- 30
2 -------------------------- 20
3---------------------------50
4-------------------------- 10
this is my code But the result does not show it:
int a = Convert.ToInt32(textBox1.Text);
var q = (from s in Session.DB.PoolUsers
where s.PoolQID == a
select s);
var qq = (from c in q
group c by c.PoolAID into agroups
select agroups.Key);
var qqq = (from c in qq
select c).Count();
MessageBox.Show(qqq.ToString());
And there is my classes:
public partial class PoolA //For answers
{
public int Id { get; set; }
public string Answer { get; set; }
public string QuestionID { get; set; }
}
public partial class PoolQ //Questions
{
public int Id { get; set; }
public string Question { get; set; }
public string AnswerID { get; set; }
public string Status { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
}
public partial class PoolUser //resaults
{
public int Id { get; set; }
public int PoolQID { get; set; }
public int PoolAID { get; set; }
public int UserID { get; set; }
}
This is not the optimal way I would do it, I would highly consider you, to change your entities.
Here is the solution:
int questionId = 1;
var questionAnswers = list.Where(elem => elem.PoolQID == questionId);
int questionAnswersCount = questionAnswers.Count();
var answersPrecentage = questionAnswers
.GroupBy(elem => elem.PoolAID)
.ToDictionary(grp => grp.Key, grp => (grp.Count() * 100.0 / questionAnswersCount));
.NET Fiddle Link
I don't know your intentions but I think you should redesign the schema (not that it not doable as it is) for maximum efficiency. You call your PoolUser table your results table but it is your answers log at best. you need a table that has the results processed already such as would be done in a DWH kind of situation.
If you have a results summary table you could work out vertical sums groups all day without the extra performance of joining tables etc. for ad hoc data. I reckon you'd be running this a lot to go over the results, so it sounds like it is a better idea to store the summarized information for reporting, performance etc.
as for the current state of the problem:
var qq = (from c in q
group c by c.PoolAID into agroups
select agroups.Key);
agroups has the answerIds grouped up for the question number you typed in.
then you are just showing the row count of the grouped up answers which shows you 2:
var qqq = (from c in qq
select c).Count();
you are telling me that your messagebox shows the number "2"
this means that people only selected 2 options to answer whatever question id you put in. you don't know whether the answer is correct or not. the last query "qqq" you have, you have the count outside so it only returns the row count. you don't want the row count. you want the answerID count after the grouping.
from c in qq
select count(c)
this is the total answers for the questionID you typed in.
now you need the correct answers in order to calculate the percentage. this is the query missing. you have gone 75% of the way. just finish it off. hopefully this is sufficient detail and not too much text to read.
The problem was solved and I got the following code. I also draw a graph based on
private void button1_Click(object sender, EventArgs e)
{
var list = (from c in Session.DB.PoolUsers
select c).ToList();
int questionId = Convert.ToInt32(textBox1.Text);
var questionAnswers = list.Where(elem => elem.PoolQID == questionId);
int questionAnswersCount = questionAnswers.Count();
var answersPrecentage = questionAnswers
.GroupBy(elem => elem.PoolAID)
.ToDictionary(grp => grp.Key, grp => (grp.Count() * 100.0 / questionAnswersCount));
dataGridView1.DataSource = answersPrecentage.ToArray();
//Draw Chart From DataGridview:
chart1.DataBindTable(answersPrecentage);
}
And works properly
Thanks all :)
I have the following class:
public partial class MemberTank
{
public int Id { get; set; }
public string TankName { get; set; }
public int Battles { get; set; }
public int Victories { get; set; }
}
I am trying to construct a LINQ that gets me a list of TankName with the victory percentage of each tank. I know I need to divide the sums of both battles and victories but I'm not sure how to do that.
Doing the sum of one or the other is easy ...
var sumVictories = (from p in db.MemberTanks
group p by p.TankName into g
select new {
Tank = g.Key,
Victories = g.Sum(p => p.Victories)
}).OrderByDescending(a => a.Victories);
... but I'm not sure how to make it sum both fields then divide them to get the percentage I need.
This should work...
var sumVictories = (from p in memberTanks
group p by p.TankName
into g
select new
{
Tank = g.Key,
Victories = g.Sum(p => p.Victories),
Avg = ((double)g.Sum(p => p.Victories) / (double)g.Sum(p => p.Battles)) * 100
}).OrderByDescending(a => a.Victories);
[edit] Cleaned the code up a little