Please help me to get my head around querying using LINQ with a GROUP and SUM.
// Query the database
IEnumerable<BestSeller> best_sellers = from bs in (db.MYDATABASE).Take(25)
where bs.COMPANY == "MY COMPANY"
group bs by bs.PRODCODE into g
orderby g.Sum(g.MQTY)
select new BestSeller()
{
product_code = ,
product_description = ,
total_quantity =
};
I wish to:
Take the top 25 items from db.MYDATABASE
Group all the results by bs.PRODCODE
Order it by the sum total for each bs.PRODCODE
Where the company is "MY COMPANY"
Then pipe the data in to my BestSeller() objects
I'm confused, because as soon as I add my group in to the mix, my bs variable becomes useless.
I'm confused, because as soon as I add my group in to the mix, my bs variable becomes useless.
Yes, because you no longer have a single item - you're now processing a sequence of groups of items. You can get at first item for each group, which I assume would be a valid way of getting at the description?
var query = from bs in db.MYDATABASE.Take(25)
where bs.COMPANY == "MY COMPANY"
group bs by bs.PRODCODE into g
orderby g.Sum(x => x.MQTY)
select new BestSeller
{
product_code = g.Key,
product_description = g.First().DESCRIPTION,
total_quantity = g.Sum(x => x.MQTY)
};
Note that without specifying an ordering, "the top 25 items from db.MYDATABASE" makes no sense. "Top" in what way? You may well want:
from bs in db.MYDATABASE.OrderByDescending(x => x.Price).Take(25)
or something similar. Note that if none of those have a company of "MY COMPANY" you'll end up with no results...
Or if you want the top 25 bestsellers, you want the "take" part at the very end:
var query = from bs in db.MYDATABASE
where bs.COMPANY == "MY COMPANY"
group bs by bs.PRODCODE into g
orderby g.Sum(x => x.MQTY) descending
select new BestSeller
{
product_code = g.Key,
product_description = g.First().DESCRIPTION,
total_quantity = g.Sum(x => x.MQTY)
};
var top25 = query.Take(25);
Related
I have the following SQL query:
select
[Event2].EventType AS 'Event type',
SUM (1) AS 'Number of events',
AVG(DATEDIFF(Second, [Event1].CreationDate, [Event2].CreationDate)) AS 'Time'
from [Event] as [Event1]
join [Event] as [Event2] on [Event1].Id = [Event2].ParentId
group by [Event2].EventTypeId;
for which I found two LINQ queries.
// This query brings the event types and the differences between the events.
var rows = from event1 in _eventRepository.AsQueryable()
join event2 in _eventRepository.AsQueryable() on event1.Id equals event2.ParentId
select new
{
EventId = event2.EventId,
TimeInSeconds = DbFunctions.DiffSeconds(event1.CreationDate, event2.CreationDate)
};
// This query groups the rows before by Event type.
var groups = (from item in rows
group item by item.EventTypeId into g
select new EventModel
{
EventTypeId = g.Key,
NumberOfEvents = g.Sum(x => 1),
Time = (int) g.Average(x => x.TimeInSeconds)
}).ToList();
I have to merge this queries into a single one.
The result must contain three elements: Event Type, The number of events, The Average of the time elapsed between the creation date of first event and the creation date of the second event.
You should just add the grouping statement to the first query (instead of the select):
var result = (from event1 in _eventRepository
join event2 in _eventRepository on event1.Id equals event2.ParentId
group new {
EventId = event2.EventId,
TimeInSeconds = DbFunctions.DiffSeconds(event1.CreationDate, event2.CreationDate)
}
by event2.EventTypeId into g
select new EventModel {
EventTypeId = g.Key,
NumberOfEvents = g.Count(),
Time = (int) g.Average(x => x.TimeInSeconds)
}).ToList();
You can also not specify the fields in the grouping but just in the select statement but left it as that is the original
I've been looking at other threads here to learn how to do a GroupBy in linq. I am following the EXACT syntax that has worked for others, but, it's not working.
Here's the query:
var results = from p in pending
group p by p.ContactID into g
let amount = g.Sum(s => s.Amount)
select new PaymentItemModel
{
ContactID = g.ContactID, // <--- Error here
Amount = amount
};
pending is a List<T> that contains, among other columns, ContactID and Amount, but those are the only two I care about for this query.
The trouble is, inside the the select new, the g. won't give me any of the columns inside the original list, pending. And when I try, I get:
IGrouping <int, LeadPurchases> does not contain a definition for ContactID, and no extension method blah blah blah...
This is the SQL I am trying to emulate:
SELECT
lp.PurchasedFromContactID,
SUM (lp.Amount)
FROM
LeadPurchases lp
GROUP BY
lp.PurchasedFromContactID
You are grouping on the basis of ContactID, so it should be the Key for the result, So you have to use g.Key instead of g.ContactID; Which means the query should be like the following:
from p in pending
group p by p.ContactID into g
let amount = g.Sum(s => s.Amount)
select new PaymentItemModel
{
ContactID = g.Key,
Amount = amount
};
updates :
If you want to perform grouping based on more than one column then the GroupBy clause will be like this:
group p by new
{
p.ContactID,
p.Field2,
p.Field3
}into g
select new PaymentItemModel()
{
ContactID = g.Key.ContactID,
anotherField = g.Key.Field2,
nextField = g.Key.Field3
};
I group the result on the customers zipcode. For each zipcode, I want to see the amount of bookings and the amount of equipment that is ordered.
So far my code looks like this:
var statistics = from b in db.Bookings
from c in db.Customers
where b.customerID == c.id
group c by c.zipcode into stat
select new {
Zipcode = stat.Key,
NumberOfBookings = stat.Count()
};
This code groups result into zipcodes and gives me the amount of bookings in each zipcode. How to get the amount of equipment also?
Rather than using joins like in SQL, you can (and it's better) use the navigation properties from your model:
var statistics =
from b in db.Bookings
group b by b.Customer.zipcode into g
select new
{
Zipcode = g.Key,
NumberOfBookings = g.Count(),
NumberOfEquipments = g.SelectMany(b => b.Equipments).Count(),
};
Note that the g variable represents a set of bookings with the same zipcode, so SelectMany is used to get all associated equipments before applying the Count operator.
Of course that's not the only way, for instance you can use Sum instead:
NumberOfEquipments = g.Sum(b => b.Equipments.Count())
i have tried to manipulate some data using linq query. but i'm not sure the way i did was the best approach.
var makes = (from m in
(from pv in vehicleViews
let make = HttpUtility.ParseQueryString(new Uri("http://www.someurl.com" + pv.Url).Query).Get("Criteria.Make")
select new
{
Name = make,
Y = pv.TotalViews,
})
group m by m.Name into g
let total = g.Sum(x => x.Y)
select new
{
Name = g.Key,
Y = total
}).OrderByDescending(x => x.Y);
is there anyway to simplified this query ?
Thanks
You do have a couple of excess projections, an unnecessary nested select before the grouping, and a use of method syntax that isn't strictly necessary. I beleive the following is equivalent to your query:
from pv in vehicleViews
let make = HttpUtility
.ParseQueryString(new Uri("http://www.someurl.com" + pv.Url).Query)
.Get("Criteria.Make")
group pv by make into g
let total = g.Sum(pv => pv.TotalViews)
orderby total descending
select new { Name = g.Key, Y = total };
The let make = line isn't strictly necessary (you could just group by that value directly) but it does make it clearer what you are calculating.
I am working on a system for handling meter reading.
I want to produce a output where the system displays all the meters belonging to the customer and for each meter, the three last readings.
So far, I have to followering code:
var lastMeterReading = from meeters in metermodel.Meeters
join reading in metermodel.Readings on meeters.MeterNumber equals reading.MeterNumber
where (maalers.CustNo == 6085574)
orderby reading.Date descending
group meeters by new { meeters.MeterNumber, reading.Consumption, reading.Date } into result
select new
{
Consumption = result.Key.Consumption, No = result.Key.MeterNumber, Date = result.Key.Date
};
Now, it shows all the meters belonging to the customer. If I put a .take(3), it only shows the first 3 results.
Thx!
Daniel
I think what you need is to put the .Take(3) in the right place.
In you case you probably did result.Take(3) but this means take the first three groups (with all their elements).
Below is an attempt to show what I mean, however, I suppose you will need to fix it in the last part, as I don't have data to test it on, and as such I'm not sure if what I'm trying to access is accessible at that point. But I hope you get what I mean.
var lastMeterReading = (from meeters in metermodel.Meeters
join reading in metermodel.Readings on meeters.MeterNumber equals reading.MeterNumber
where (maalers.CustNo == 6085574)
orderby reading.Date descending
group meeters by new { meeters.MeterNumber, reading.Consumption, reading.Date } into result
from m in result
select new {Key = m.Key, Info = result.OrderByDescending(r => r.Date).Take(3)})
.Select(r => new
{ Consumption = r.Consumption, No = r.MeterNumber, Date = r.Date });
Try this:
var lastMeterReading = from meeters in metermodel.Meeters
join reading in metermodel.Readings on meeters.MeterNumber equals reading.MeterNumber
where (maalers.CustNo == 6085574)
orderby reading.Date descending
group meeters by new { meeters.MeterNumber, reading.Consumption, reading.Date } into result
from m in result.Take(3)
select new
{
Consumption = m.Consumption, No = m.MeterNumber, Date = m.Date
};
You only want to group by MeterNumber. The way you're doing the grouping right now, you'll get a new group for every unique MeterNumber-Consumption-Date combination.
You can also simplify your query using LINQ's GroupJoin operator. In query syntax you use the "join..on..into" pattern:
from meter in meterModel.Meters
where (meter.CustNo == 6085574)
join reading in meterModel.Readings
on meter.MeterNumber equals reading.MeterNumber
into meterGroup
select meterGroup.OrderByDescending(r => r.Date).Take(3);
Or using dot notation:
meterModel.Meters
.Where(x => x.CustNo == 6085574)
.GroupJoin(
meterModel.Readings,
meter => meter.MeterNumber,
reading => reading.MeterNumber,
(meter,readings) => readings.OrderByDescending(r => r.Date).Take(3))
;