I am trying to write equivalent linq code for following query.
SELECT A.*
FROM
(
SELECT * FROM TableA
WHERE id = 100
) a
JOIN
(
SELECT Name, MAX(AnotherId) AnotherId
FROM TableA
WHERE id = 100
GROUP BY Name
) b
on a.Name = b.Name and a.AnotherId = b.AnotherId
This is the linq
var Collection = from R in DbContext.TableA
join G in (DbContext.TableA.Where(r => r.Id == 100).GroupBy(r => new { r.Name, r.AnotherId } ).Select(g => new { Name = g.Name , AnotherId = g.Max(o => o.AnotherId) }))
on new { R.Name, R.AnotherId } equals new { G.Name, G.AnotherId }
where R.Id == 100
select R;
But I am getting following compilation error that I don’t know how to fix. Any thoughts
The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
Error 7 'System.Linq.IGrouping' does not contain a definition for 'Name' and no extension method 'Name' accepting a first argument of type 'System.Linq.IGrouping' could be found (are you missing a using directive or an assembly reference?)
You group by r.Name, r.AnotherId, when you just want to group by r.Name.
var Collection = from R in DbContext.TableA
join G in (DbContext.TableA
.Where(r => r.Id == 100)
.GroupBy(r => r.Name)
.Select(g => new { Name = g.Key , AnotherId = g.Max(o => o.AnotherId) }))
on new { R.Name, R.AnotherId } equals new { G.Name, G.AnotherId }
where R.Id == 100
select R;
And to have all in Fluent Syntax
var collection = DbContext.TableA
.Where(t1 => t1.Id == 100)
.Join(DbContext.TableA
.Where(t2 => t2.Id == 100)
.GroupBy(t2 => t2.Name)
.Select(group => new{Name = group.Key,
AnotherId = group.Max(e => e.AnotherId)})
),
t1 => new{t1.Name, t1.AnotherId} ,
t2 => new{t2.Name, t2.AnotherId},
(t1, t2) => t1);
llHi you need the following syntax, notice the addition of 'Key'
var Collection = from R in DbContext.TableA
join G in (DbContext.TableA.Where(r => r.Id == 100)
.GroupBy(r => new { r.Name, r.AnotherId } )
.Select(g => new { Name = g.Key.Name , AnotherId = g.Max(o => o.AnotherId) }))
on new { R.Name, R.AnotherId } equals new { G.Name, G.AnotherId }
where R.Id == 100
select R;
I would recommend using the query syntax for all parts of your query. By doing it this way, your linq query will have a more similar structure to your original sql query. It would look like this:
var query =
from a in
(from x in DbContext.TableA
where x.ID == 100
select x)
join b in
(from x in DbContext.TableA
where x.ID == 100
group x by x.Name into x
select new
{
Name = x.Key,
AnotherId = x.Max(o => o.AnotherId),
})
on new { a.Name, a.AnotherId } equals new { b.Name, b.AnotherId }
select a;
saj and Raphael both have found good points:
.GroupBy(r => new { r.Name, r.AnotherId } )
.Select(g => new { Name = g.Name , AnotherId = g.Max(o => o.AnotherId) }))
Groups don't have a Name. Each group has a Key (and the Key has a Name and AnotherId).
Since you want the Max(AnotherId), you don't want to include AnotherId in your grouping Key (same as it is not present in the GroupBy clause of the original query).
.GroupBy(r => r.Name) //the Name is the Key
.Select(g => new { Name = g.Key, AnotherId = g.Max(o => o.AnotherId) }))
Related
Need some help translating below query to LINQ.
;With BookingWithLastStatus
as
(
Select *, Rnk = ROW_NUMBER() over (partition by BookingId order by Id desc)
from BookingStatus
)
Select *
from BookingWithLastStatus
where Rnk=1 AND StatusId = 3
I've done LINQ below but it is not getting the correct records.
var BookStatus = from p in _context.Set<BookingStatus>()
where p.StatusId == 3
group p by p.BookingId into opt
select new {
BookingId = opt.Key,
Id = opt.Max(x => x.Id)
};
The SQL query is getting 1 record only which is correct and my LINQ is getting multiple records.
UPDATE:
I did like this:
Get all the BookingStatus first
var GetAllBookStatus = await _context.Set<BookingStatus>()
.ToListAsync();
Then do the filtering based from the SQL Query I need.
var FilteredBookStatus = GetAllBookStatus
.OrderByDescending( x => x.Id )
.GroupBy(person => person.BookingId)
.Select( group => new { Group = group, Count = group.Count() } )
.SelectMany( groupWithCount =>
groupWithCount.Group.Select( b => b)
.Zip(
Enumerable.Range( 1, groupWithCount.Count ),
( b, i ) => new {
b.Id,
b.BookingId,
b.BookingMWABId,
b.BookStatus,
b.CreatedBy,
b.CreatedDate,
b.Destination,
b.InternalStatus,
b.LineNum,
b.ModifiedBy,
b.ModifiedDate,
b.Module,
b.ReasonCode,
b.ReceivedBy,
b.RefNo,
b.StatusId,
b.TimeStamp,
RowNumber = i }
)
)
.Where(a => a.StatusId == 3 && a.RowNumber == 1)
.ToList();
But I'm not so confident on Getting all records, as it will grow some time. Is there anything I can change from my code?
With EF core 6.x, you can do the following. It is not optimal case as in your SQL, but should work:
var BookStatus =
from p in _context.Set<BookingStatus>()
group p by p.BookingId into g
select g.OrderByDescending(x => x.Id).First();
BookStatus = BookStatus.Where(p => p.StatusId == 3);
Or another variant
var BookStatus = _context.Set<BookingStatus>().AsQueryable();
BookStatus =
from d in BookStatus.Select(d => new { d.BookingId }).Distinct()
from p in BookStatus
.Where(p => p.BookingId == d.BookingId)
.OrderByDescending(p => p.Id)
.Take(1)
select p;
BookStatus = BookStatus.Where(p => p.StatusId == 3);
I have a problem to write in lambda query this sql query:
select c.Id, c.Name, c.SomeNumber, count(*) from TableA a
inner join TableB b
on a.Id = b.aId
inner join TableC c
on c.BId = b.Id
where b.Status = N'Approved'
and c.Scope = N'Scope1'
group by a.Id, a.Name, a.SomeNumber
Can you guys help me with this one ? I want to write lambda query to execute this in code. I'm using EF Core 3.1
This is what I ended up so far:
var query = await _dbContext.TableA.Where(a => a.TableB.Any(b => b.Status.Equals("Approved")
&& b.TableC.Any(c => c.Scope.Equals("Scope1"))))
.GroupBy(g => new { Id = g.Id, Name = g.Name, SomeNumber = g.SomeNumber })
.Select(s => new { Id = s.Key.Id, Name = s.Key.Name, SomeNumber = s.Key.SomeNumber, Count = s.Count() })
.GroupBy(g => g.Id).Select(s => new {Id = s.Key, Count = s.Count()}).ToListAsync();
Well, this is corrected query. I have used Query syntax which is more readable when query has lot of joins or SelectMany.
var query =
from a in _dbContext.TableA
from b in a.TableB
from c in b.TableC
where b.Status == "Approved" && c.Scope == "Scope1"
group a by new { a.Id, a.Name, a.SomeNumber } into g
select new
{
g.Key.Id,
g.Key.Name,
g.Key.SomeNumber,
Count = g.Count()
}
var result = await query.ToListAsync();
It's maybe easier to start from the many end and work up through the navigation properties
tableC
.Where(c => c.Scope == "Scope1" && c.BEntity.Status == "Approved")
.GroupBy(c => new
{
c.BEntity.AEntity.Id,
c.BEntity.AEntity.Name,
c.BEntity.AEntity.SomeNumber
})
.Select(g => new { g.Key.Id, g.Key.Name, g.Key.SomeNumber, Ct = g.Count()})
EF knows how to do joins when you navigate around the object tree in the where. By starting at the many and and working up to the 1 end of the relationship it means you don't have to get complex with asking "do any of the children of this parent have a status of ..."
I have an EF query that contains a number of aggregate sub queries (Count, Sum and Max). There are two problems.
I need the joins on the sub queries to be left joins so that records are returned even if there are no results in the aggregate sub queries. At present, records are only returned if all sub queries return records.
The resulting list of WholesaleCustomerAndAggregateOrders objects that are returned contain a Contact object that needs to also include Addresses and Counties. I added Include(c => c.Addresses.Select(a => a.Country)) to the query but the Contact objects don't contain any Address objects.
Any assistance with either issue would be appreciated. Full query below.
var month1Date = DateTime.Today.AddMonths(-1);
var month3Date = DateTime.Today.AddMonths(-3);
var month6Date = DateTime.Today.AddMonths(-6);
var month12Date = DateTime.Today.AddMonths(-12);
var db = GetNewContext();
var qry = from c in db.Contacts
.Include(c => c.Addresses.Select(a => a.Country))
join orderCount in
(
from o in db.WholesaleOrders
group o by o.ContactId into g
select new
{
ContactId = g.Key,
TotalOrders = g.Count()
}
) on c.Id equals orderCount.ContactId
join month1Value in
(
from o in db.WholesaleOrders
where o.OrderDate >= month1Date
group o by o.ContactId into g
select new
{
ContactId = g.Key,
TotalValue = g.Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price))
}
) on c.Id equals month1Value.ContactId
join month3Value in
(
from o in db.WholesaleOrders
where o.OrderDate >= month3Date
group o by o.ContactId into g
select new
{
ContactId = g.Key,
TotalValue = g.Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price))
}
) on c.Id equals month3Value.ContactId
join month6Value in
(
from o in db.WholesaleOrders
where o.OrderDate >= month6Date
group o by o.ContactId into g
select new
{
ContactId = g.Key,
TotalValue = g.Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price))
}
) on c.Id equals month6Value.ContactId
join month12Value in
(
from o in db.WholesaleOrders
where o.OrderDate >= month12Date
group o by o.ContactId into g
select new
{
ContactId = g.Key,
TotalValue = g.Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price))
}
) on c.Id equals month12Value.ContactId
join month12Quantity in
(
from o in db.WholesaleOrders
where o.OrderDate >= month12Date
group o by o.ContactId into g
select new
{
ContactId = g.Key,
OrderCount = g.Count()
}
) on c.Id equals month12Quantity.ContactId
join lastOrderDate in
(
from o in db.WholesaleOrders
group o by o.ContactId into g
select new
{
ContactId = g.Key,
LastOrderDate = g.Max(r => r.OrderDate)
}
) on c.Id equals lastOrderDate.ContactId
select new WholesaleCustomerAndAggregateOrders
{
Contact = c,
TotalOrders = orderCount.TotalOrders,
Month1Value = month1Value.TotalValue,
Month3Value = month3Value.TotalValue,
Month6Value = month6Value.TotalValue,
Month12Value = month12Value.TotalValue,
Month12OrderCount = month12Quantity.OrderCount,
LastOrderDate = lastOrderDate.LastOrderDate
};
return await qry.ToListAsync();
How about this:
db.WholesaleOrders
.GroupBy(o => o.ContactId)
.Select(a => new {
a.Key,
TotalOrders = a.Count(),
LastOrderDate = a.Max(r => r.OrderDate),
Month1Value = a.Where(b => b.OrderDate >= month1Date).Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price),
Month3Value = a.Where(b => b.OrderDate >= month3Date).Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price),
Month6Value = a.Where(b => b.OrderDate >= month6Date).Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price),
Month12Value = a.Where(b => b.OrderDate >= month12Date).Sum(r => r.LineItems.Sum(l => l.QuantityOrdered * l.Price)
}).ToListAsync();
UPDATE: Add another property to projection:
Addresses = db.Addresses.Where(ad => ad.ContactId == a.Key);
where db is your context, and ad.ContactId is FK of the contact in Address table.
For this to work, multipleactiveresultsets property of the connection must be set to True.
I have an SQL expression
select S.SpecialtyName, COUNT(distinct SUC.SiteUserId) as Subscribers
from SiteUserContent SUC Inner join
Specialties S on SUC.SpecialtyId = S.SpecialtyId Inner join
SiteUser SU on SUC.SiteUserId = SU.SiteUserId
where SU.DeletedFlag = 0
group by S.SpecialtyName
Order by S.SpecialtyName
What will be the corresponding LINQ expression for the same?
from suc in context.SiteUserContent
join s in context.Specialties on suc.SpecialtyId equals s.SpecialtyId
join su in context.SiteUser on suc.SiteUserId equals su.SiteUserId
where su.DeletedFlag == 0
select new { suc.SiteUserId, s.SpecialityName } into x
group x by x.SpecialityName into g
orderby g.Key
select new {
SpecialityName = g.Key,
Subscribers = g.Select(i => i.SiteUserId).Distinct().Count()
}
Generated SQL will not be same, but I think result of query execution should be same.
var results = contex.SiteUserContent
.Join(context.Specialties, suc => suc.SpecialtyId, s => s.SpecialtyId, (suc, s) => new { suc, s })
.Join(context.SiteUser, i = i.suc.SiteUserId, su => su.SiteUserId, (i, su) => new { suc = i.suc, s = i.s, su = su })
.Where(i => i.su.DeletedFlag == 0)
.GroupBy(i => i.s.SpecialtyName)
.Select(g => new {
SpecialityName = g.Key,
Subscribers = g.Select(i => i.suc.SiteUserId)
.Distinct()
.Count()
})
.OrderBy(i => i.SpecialityName);
I have some SQL and am trying to make the equivalent in LINQ. This is the SQL:
SELECT Categories.CategoryDescription, Categories.CategoryType AS Type,
Categories.Category, COUNT(CategoryLinks.OrgID) AS CountOfOrgs
FROM CategoryLinks
INNER JOIN Categories ON Categories.CategoryID = CategoryLinks.CategoryID
GROUP BY Categories.Category, Categories.CategoryType, Categories.CategoryDescription
ORDER BY CategoryDescription ASC
Essentially, I want a list of everything from the Categories table and a count of the number of OrgId's in the CategoryLinks table that links to it.
Below is the query I am performing at the moment. There has to be a more efficient way to do this. Am I wrong?
var cnts = (from c in db.Categories
join cl in db.CategoryLinks on c.CategoryID equals cl.CategoryID
group new { c, cl } by new
{
c.CategoryID
} into g
select new
{
CategoryID = g.Key.CategoryID,
categoryCount = g.Count()
});
var results = (from c in db.Categories
join cn in cnts on c.CategoryID equals cn.CategoryID
select new
{
c.CategoryID,
c.CategoryDescription,
c.CategoryType,
Category = c.Category1,
cn.categoryCount
});
I think you want to use the GroupJoin method:
Categories.GroupJoin(
CategoryLinks,
x => x.CategoryID,
y => y.CategoryID,
(x,y) => new{
x.CategoryID,
x.CategoryDescription,
x.CategoryType,
Category = x.Category1,
CategoryCount = y.Count() })
In query syntax, this is written as join..into:
from c in db.Categories
join cl in db.CategoryLinks on c.CategoryID equals cl.CategoryID into catGroup
select new
{
c.CategoryID,
c.CategoryDescription,
c.CategoryType,
Category = c.Category1,
CategoryCount = catGroup.Count()
}
Try this:
var bbb = categories.Join(categoryLinks, c => c.CategoryID, cl => cl.CategoryId, (c, cl) => new {c, cl})
.GroupBy(g => g.c)
.Select(g => new {count = g.Count(), Category = g.Key});
It returns count and all data that is in Category. We group by all columns in category and place result in new anonymous type variable that contains 2 properties: Count, that contains count and Category that is of type Category and contains all data that is in category row.
If you want, you can rewrite it as:
var bbb = categories.Join(categoryLinks, c => c.CategoryID, cl => cl.CategoryId, (c, cl) => new {c, cl})
.GroupBy(g => g.c)
.Select(g => new
{
CategoryID = g.Key.CategoryId,
CategoryDescription = g.Key.CategoryDescription,
CategoryType = g.Key.CategoryType,
Category = g.Key.Category1,
categoryCount = g.Count()
});