var storeIds = repository.Get<Store>()
.Select(s => s.Id)
.ToList();
var storeReceipts = repository.Get<Receipt>()
.Where(r => DbFunctions.TruncateTime(r.LogDate) == today)
.GroupBy(r => r.StoreId)
.Select(g => new { Id = g.Key, Sales = g.Sum(r => r.TotalPrice) })
.GroupJoin(storeIds, x => x.Id, s => s, (x, s) => x ?? new { Id = s, Sales = 0 });
Basically I want the GroupJoin to add an entry to the sequence for any Store that doesn't have Receipt records.
My syntax above with the ?? doesn't compile (even if it did I am not sure its correct).
If you want to join the two tables, you may want to try this.
var storeSales = from s in repository.Get<Store>()
join r in repository.Get<Receipt>() on s.Id equals r.StoreId into g
select new {
StoreId = s.Id,
Sales = g.Sum(x => (decimal?)x.TotalPrice) ?? 0
};
It selects from the Stores first, so that you will get an entry for each store. It will next join the Receipts by matching store id into a group g that joins the two.
That allows the selection of your output shape, one item per store. In this case, the Id of the store as StoreId, and the sum of the TotalPrice values for each receipt as the Sales are selected.
If there were no receipts for a store, this sum will end up being 0.
Related
I am using Xamarin c# linq with sqlite-net pcl (https://github.com/praeclarum/sqlite-net). I found that all my linq groupby cannot be properly translated into SQL. Linq translates the Where clause but not Group By.
In the following example, the fields used in Transaction:
AccountId: int
Amount: double
ICategoryId: int
All these query formats result in SQL without Group By:
select * from "Transaction" where ("DateWithoutTime" <= ?)
var accountBalances1 = _dataService.Connection.Table<Transaction>()
.Where(r => r.DateWithoutTime <= displayDuration.DurationEnd)
.GroupBy(r => r.AccountId)
.Select(g => new
{
Id = g.Key,
Balance = g.Sum(b => b.Amount)
})
.ToList();
var accountBalances2 = _dataService.Connection.Table<Transaction>()
.Where(r => r.DateWithoutTime <= displayDuration.DurationEnd)
.GroupBy(r => r.AccountId,
(key, g) => new
{
Id = key,
Balance = g.Sum(b => b.Amount),
})
.ToList();
var accountBalances3 = (from t in _dataService.Connection.Table<Transaction>()
where t.DateWithoutTime <= displayDuration.DurationEnd
group t by t.AccountId
into g
select new { g.Key, Balance = g.Sum(g => g.Amount) })
.ToList();
To clarify it has nothing to do with double data type, I tried another group by with int data type only:
var maxIECategoryId = _dataService.Connection.Table<Transaction>()
.Where(r => r.DateWithoutTime <= displayDuration.DurationEnd)
.GroupBy(r => r.AccountId,
(key, g) => new
{
Id = key,
IECategoryId = g.Max(b => b.IECategoryId)
})
.ToList();
Similarly, the generated sql does not have group by.
All group by are processed locally. Is there any trick to write a linq group by that can be processed on the server service? or it is a limitation of this implementation of sqlite orm?
Thanks,
Nick
I have this query that I need to run:
IEnumerable<MerchantWithParameters> merchants =
from i in (
from m in d.GetTable<Merchant>()
join mtp in d.GetTable<MerchantToParameters>() on m.Id equals mtp.MerchantId into mtps
from mtp in mtps.DefaultIfEmpty()
join cp in d.GetTable<ContextParameters>() on mtp.ContextParametersId equals cp.Id into cps
from cp in cps.DefaultIfEmpty()
select new {Merchant = m, ContextParameter = cp}
)
group i by new { i.Merchant.Id } into ig
select new MerchantWithParameters()
{
Id = ig.Key.Id,
Parameters = ig.Where(g => g.ContextParameter != null).ToDictionary(g => g.ContextParameter.Key, g => g.ContextParameter.Text)
};
For some reason it takes really long time for this query to be completed.
I believe that it has something to do with
Parameters = ig.Where(g => g.ContextParameter != null).ToDictionary(g => g.ContextParameter.Key, g => g.ContextParameter.Text)
Because when I remove this line, query starts to execute really fast.
Could you please show me what am I doing wrong?
UPDATE:
I am using ToList() to extract data from the database.
It is known SQL limitation. You cannot get grouped items, only grouping key or aggregation result. Since you need all records, we can do grouping on the client side, but previously maximally limit retrieved data.
var query =
from m in d.GetTable<Merchant>()
from mtp in d.GetTable<MerchantToParameters>().LeftJoin(mtp => m.Id == mtp.MerchantId)
from cp in d.GetTable<ContextParameters>().LeftJoin(cp => mtp.ContextParametersId == cp.Id)
select new
{
MerchantId = m.Id,
ContextParameterKey = (int?)cp.Key,
ContextParameterText = cp.Text
};
var result =
from q in query.AsEnumerable()
group q by q.MerchantId into g
select new MerchantWithParameters
{
Id = g.Key,
Parameters = g.Where(g => g.ContextParameterKey != null)
.ToDictionary(g => g.ContextParameterKey.Value, g => g.ContextParameterText)
};
var merchants = result.ToList();
I want to group by a table with Order Id but if one of price is negative don’t group by and brings all rows in output
I use below code but group by all order id
tblResult = tblResult.AsEnumerable().GroupBy(r => new { orderId = r["OrderID"] }).Select(g =>
{
var row = tblResult.NewRow();
row["Order ID"] = g.Key.orderId;
row["Price"] = g.Sum(r => float.Parse(r.Field<string>("Price"))).ToString();
return row;
}).CopyToDataTable();
You can create your condition in grouping, the tricky part is the result would be a list for those with negative prices and single item for those without it. if we also make single items as list then SelectMany() shoud do what you want:
var result = list.GroupBy(x => x.Id)
.SelectMany(g => g.Any(x => x.Price < 0)?
g.ToList():
new List<Order> { new Order { Id = g.Key, Price = g.Sum(grp => grp.Price)}});
LIVE DEMO
I search more on this site for "get 4 top item from each group", but there are many topic about get first item from each group like this
var rr = db.Products
.GroupBy(x => x.ProductSubTypeCategoryId, (key, g) => g.OrderBy(e => e.PersianName)
.Take(4).ToList());
or
var rr = db.Products
.GroupBy(x => x.ProductSubTypeCategoryId).Select(g => new { pname = g.Key, count = g.Count() });
but return only first item from each group. How can I change the code to get 4 items from each group?
Try this:
var rr = db.Products.GroupBy(x => x.ProductSubTypeCategoryId).Select(g => new { GroupName = g.Key, Items = g.Take(4).ToList() });
This should give you an anonymous object with a GroupName property that returns the ProductSubTypeCategoryId and an Items property that returns a list of up to 4 items for each group.
Try something like this with SelectMany()
var rr = db.Products
.GroupBy(x => x.ProductSubTypeCategoryId)
.SelectMany(g => g.OrderBy(e => e.PersianName).Take(4))
.ToList();
I have two tables:
tblBadge
-------
ID
Name
tblBadgeUsers
-------
BadgeID
UserID
A user can have many badges, all the relationships are set up properly in the SQL database.
I'm trying to return a list of all the badges a user has, along with the total number of those badges. This is about as far as I can get, I'm getting pretty confused. I want to return the tblBadge data along with an extra column showing the total of that badge awarded.
This doesn't work but is my attempt:
var q = db.tblBadgeUsers
.Where(c => c.UserID == UserID)
.GroupBy(c => c.BadgeID)
.Select(c => new { BadgeCount = c.Count(), Record = c.tblBadge });
Given that a badge only really has a badge ID and a user, it sounds like you just need to get the badge ID and the count of that badge for the use - which means just getting the key for the group:
var q = db.tblBadgeUsers
.Where(c => c.UserID == UserID)
.GroupBy(c => c.BadgeID)
.Select(c => new { BadgeCount = c.Count(), BadgeId = c.Key });
If there's more information on each badge, you might want to do:
var q = db.tblBadgeUsers
.Where(c => c.UserID == UserID)
.GroupBy(c => c.BadgeID)
.Select(c => new { BadgeCount = c.Count(), BadgeId = c.Key, Badges = c });
Then you could do:
foreach (var badgeType in q)
{
Console.WriteLine("{0}: {1}", badgeType.BadgeId, badgeType.BadgeCount);
foreach (var badge in q.Badges)
{
// Deal with the badge information
}
}