Using a SQL query within Microsoft SQL Server, I am able to get my desired results. Now I'm trying to utilize this query in my project via LINQ.
My SQL query is
SELECT distinct DeviceId, max (Head), max(Shoulder), max(Chest)
FROM EventUserOverPressure eop
JOIN UserEventInfo uei on uei.UserEventInfo_Id = eop.UserEventInfo_Id
JOIN BlastRecord br ON br.BlastRecord_Id = uei.BlastRecord_Id
JOIN WeaponsFiringLog wfl ON wfl.BlastRecord_Id = br.BlastRecord_Id
JOIN WeaponsFired wf ON wf.Blast_WFL_Id = wfl.Blast_WFL_Id
WHERE br.BlastRecord_Id = 1599
group BY DeviceId
Thus far, my LINQ query is
var myOverPressures = (from eop in db.EventUserOverPressures
join uei in ueiList on eop.UserEventInfo_Id equals uei.UserEventInfo_Id
join br in blastRecords on uei.BlastRecord_Id equals br
join wfl in weaponFiringLogss on uei.BlastRecord_Id equals wfl.BlastRecord_Id
join wf in weaponsFired on wfl.Blast_WFL_Id equals wf.Blast_WFL_Id
where (eop.Chest > 0 || eop.Head > 0 || eop.Shoulder > 0)
select new { eop.DeviceDataId, eop.Head, eop.Shoulder, eop.Chest }).Distinct().ToList();
I know the BlastRecord_Id is set to 1599 and it's a variable in LINQ. That's intentional. I was trying to figure out my query in SQL, so I focused on specific record. In LINQ it needs to work for all BlastRecord_Id's. Using LINQ, I'm able to group by DeviceDataId on the next, outside of the initial query.
My goal is to group by DeviceDataId as part of this query, and get the max values for Head Shoulder and Chest - like I did in the SQL query. If it matters, my end goal is sort my results. I know my SQL query results give me what I need in order to sort how I want. I've spent an embarrassing amount of time trying to figure this out. Any help is greatly appreciated.
Try:
var myOverPressures = (
from eop in db.EventUserOverPressures
join uei in ueiList on eop.UserEventInfo_Id equals uei.UserEventInfo_Id
join br in blastRecords on uei.BlastRecord_Id equals br.BlastRecord_Id
join wfl in weaponFiringLogss on uei.BlastRecord_Id equals wfl.BlastRecord_Id
join wf in weaponsFired on wfl.Blast_WFL_Id equals wf.Blast_WFL_Id
where (eop.Chest > 0 || eop.Head > 0 || eop.Shoulder > 0)
select new { eop.DeviceDataId, eop.Head, eop.Shoulder, eop.Chest }
)
.GroupBy(r => r.DeviceDataId)
.Select(g => new {
DeviceDataId = g.Key,
maxHead = g.Max(r => r.Head),
maxShoulder = g.Max(r => r.Shoulder),
maxChest = g.Max(r => r.Chest)
})
.ToList();
The .GroupBy() maps the data to a collection of groups, each of which has a key and a collection of group member objects. The .Select() then extracts the key and calculates the max of the Head/Shoulder/Chest values within each group.
I removed the .Distinct(), as I believe it is unnecesary due to the fact that each group key (DeviceDataId) should already be distinct.
As a side note: I noticed that the join structure of your query has what appears to be two independent one-to-many join relationships:
BlastRecord
+--> UserEventInfo --> EventUserOverPressure
+--> WeaponsFiringLog --> WeaponsFired
This may lead to the results being the cartesian product of the two join paths, yielding duplicate data. This could be a problem if you were counting of summing the effects, but if max() is the only aggregation used, I do not believe the results are affected.
I believe the grouping and aggregation may also be done in the LINQ query syntax. Something like:
var myOverPressures = (
...
group eop by eop.DeviceDataId into g
select new {
DeviceDataId = g.Key,
maxHead = g.Max(r => r.Head),
maxShoulder = g.Max(r => r.Shoulder),
maxChest = g.Max(r => r.Chest)
}
)
.ToList();
(I am not 100% sure I have this right. If someone spots an error and comments, I will correct the above.)
Try this one
var query = (from eop in db.EventUserOverPressure
join uei in db.UserEventInfo on eop.UserEventInfo_Id equals uei.UserEventInfo_Id
join br in db.BlastRecord on uei.BlastRecord_Id equals br.BlastRecord_Id
join wfl in db.WeaponsFiringLog on br.BlastRecord_Id equals wfl.BlastRecord_Id
join wf in db.WeaponsFired on wfl.Blast_WFL_Id equals wf.Blast_WFL_Id
where br.BlastRecord_Id == 1599
group eop by eop.DeviceId into g
select new
{
DeviceId = g.Key,
Head = g.Max(x => x.Head),
Shoulder = g.Max(x => x.Shoulder),
Chest = g.Max(x => x.Chest)
});
Related
I have a Linq query which is being used to replace a database function. This is the first one with multiple joins and I can't seem to figure out why it returns 0 results.
If you can see any difference which could result in the incorrect return it would be greatly appreciated......I've been trying to solve it longer than I should have.
Linq Query
context.StorageAreaRacks
.Join(context.StorageAreas, sar => sar.StorageAreaId, sa => sa.Id, (sar, sa) => new { sar, sa })
.Join(context.StorageAreaTypes, xsar => xsar.sar.StorageAreaId, sat => sat.Id, (xsar, sat) => new { xsar, sat })
.Join(context.Racks, xxsar => xxsar.xsar.sar.RackId, r => r.Id, (xxsar, r) => new { xxsar, r })
.Where(x => x.xxsar.sat.IsManual == false)
.Where(x => x.r.IsEnabled == true)
.Where(x => x.r.IsVirtual == false)
.Select(x => new { x.xxsar.sat.Id, x.xxsar.sat.Name })
.Distinct()
.ToList();
This is the query which is generated by the LINQ query
SELECT
[Distinct1].[C1] AS [C1],
[Distinct1].[Id] AS [Id],
[Distinct1].[Name] AS [Name]
FROM ( SELECT DISTINCT
[Extent2].[Id] AS [Id],
[Extent2].[Name] AS [Name],
1 AS [C1]
FROM [dbo].[StorageAreaRacks] AS [Extent1]
INNER JOIN [dbo].[StorageAreaTypes] AS [Extent2] ON [Extent1].[StorageAreaId] = [Extent2].[Id]
INNER JOIN [dbo].[Racks] AS [Extent3] ON [Extent1].[RackId] = [Extent3].[Id]
WHERE (0 = [Extent2].[IsManual]) AND (1 = [Extent3].[IsEnabled]) AND (0 = [Extent3].[IsVirtual])
) AS [Distinct1]
Sql Query which produces required results
SELECT DISTINCT sat.Name, sat.Id
FROM StorageAreaRacks sar
JOIN StorageAreas sa on sa.id = sar.StorageAreaId
JOIN StorageAreaTypes sat on sat.id = sa.StorageAreaTypeId
JOIN Racks r on r.id = sar.RackId
WHERE sat.IsManual = 0
AND r.IsEnabled = 1
AND r.IsVirtual = 0
Using joins with LINQ method syntax is hard to read and error prone.
Using joins with LINQ query syntax is better, but still error prone (you can join by the wrong key as you did) and does not give you information about join cardinality.
The best for LINQ to Entities queries is to use navigation properties (as Gert Arnold suggested in the comments and not only - see Don’t use Linq’s Join. Navigate!) because they have none of the aforementioned drawbacks.
The whole query should be something like this:
var query = context.StorageAreaRacks
.Where(sar => !sar.StorageArea.StorageAreaType.IsManual
&& sar.Rack.IsEnabled && !sar.Rack.IsVirtual)
.Select(sar => new
{
sar.StorageArea.StorageAreaType.Id,
sar.StorageArea.StorageAreaType.Name,
})
.Distinct();
or
var query = (
from sar in context.StorageAreaRacks
let sat = sar.StorageArea.StorageAreaType
let r = sar.Rack
where !sat.IsManual && r.IsEnabled && !r.IsVirtual
select new { sat.Id, sat.Name })
.Distinct();
Simple, readable and almost no place for mistakes. Navigation properties are one of the most beautiful features of EF, don't miss them.
Your LINQ doesn't translate the SQL properly; it Joins the StorageAreaTypes on the StorageAreaRack.StorageAreaId instead of on the StorageAreas.StorageAreaTypeId, which is why EF drops the StorageAreas Join - it has no effect on the outcome.
I think it is clearer if you elevate the members of each join to flatten the anonymous objects and name them based on their members (that are the join tables). Also, no reason to separate the Where clauses, LINQ can use && as well as SQL using AND. Also, if you have boolean values, don't compare them to true or false. Also there is no reason to pass range variables through that aren't used later.
Putting it all together:
var ans = context.StorageAreaRacks
.Join(context.StorageAreas, sar => sar.StorageAreaId, sa => sa.Id, (sar, sa) => new { sar, sa })
.Join(context.StorageAreaTypes, sarsa => sarsa.sa.StorageAreaTypeId, sat => sat.Id, (sarsa, sat) => new { sarsa.sar, sat })
.Join(context.Racks, sarsat => sarsat.sar.RackId, r => r.Id, (sarsat, r) => new { sarsat.sat, r })
.Where(satr => !satr.sat.IsManual && satr.r.IsEnabled && !satr.r.IsVirtual)
.Select(satr => new { satr.sat.Id, satr.sat.Name })
.Distinct()
.ToList();
However, I think when multiple joins are involved and when translating SQL, LINQ comprehension syntax can be easier to understand:
var ans = (from sar in context.StorageAreaRacks
join sa in context.StorageAreas on sar.StorageAreaId equals sa.Id
join sat in context.StorageAreaTypes on sa.StorageAreaTypeId equals sat.Id
join r in context.Racks on sar.RackId equals r.Id
where !sat.IsManual && r.IsEnabled && !r.IsVirtual
select new {
sat.Name,
sat.Id
}).Distinct().ToList();
You are missing a Where for your rack ID != null in your LINQ statement, and a Distinct().
I have a SQL query which includes a left join and a group by- so far so good- my trouble arises from one of the join conditions not being a straight "equals to" and I'm lost where to go with LINQ.
I know multiple join conditions usually involves creating a couple of anonymous objects and comparing them, but when I add an "equal to" and "a greater" than into the mix, I've no idea how that applies.
Here's what I'd like the query to look like if I had invented LINQ, but I know the "and" in my join condition is invalid;
var query =
from csp in db.ChatSessionPersons
join cm in db.ChatMessages on
csp.ChatSessionId equals cm.ChatSessionId
and cm.Id > csp.LastReadChatMessageId
// (i know these should be the other way round,
// but for readability I present them like this!)
into j1
from j2 in j1.DefaultIfEmpty()
group j2 by csp.ChatSessionId into grouped
select new {
ChatSessionId = grouped.Key,
UnreadCount = grouped.Count(t => t.Id != null)};
Any ideas anyone?
You can convert the non-equality condition to a lambda Where on the group join result.
var query = from csp in db.ChatSessionPersons
join cm in db.ChatMessages on csp.ChatSessionId equals cm.ChatSessionId into cmj
select new {
ChatSessionId = csp.ChatSessionId,
UnreadCount = cmj.Where(cm => cm.Id > csp.LastReadChatMessageId).Count()
};
NOTE: I modified the query a bit to remove the group by which isn't needed if you are using a group join that has already grouped the matching results, and to remove the left join DefaultIfEmpty which also isn't needed when processing a group join with something like Count, unless you wanted to return an UnreadCount of 1 when there are no matches, in which case you should put DefaultIfEmpty() before Count().
Of course, you could use query comprehension in the sub-query:
var query = from csp in db.ChatSessionPersons
join cm in db.ChatMessages on csp.ChatSessionId equals cm.ChatSessionId into cmj
select new {
ChatSessionId = csp.ChatSessionId,
UnreadCount = (from cm in cmj where cm.Id > csp.LastReadChatMessageId select cm).Count()
};
I am trying to perform an outer join in C# using Linq, the person mentoring me keeps saying I shouldn't try to do an outer join which isn't really an answer.
What I got from the other threads is that I need the .DefaultIfEmpty() where ever I may not have a record.
I tried it first on just the lines where there may be a missing information then added it to every line just to see if that was the problem.
Every time I run this I get only the inner join records. It works great other than it is not including the two records from my DB that only have information in the first two tables.
var sqlQuery =
from s in ctx.Suppliers
from sp in ctx.SupplierParts
.Where(sp => sp.SupplierID == s.SupplierID)
.DefaultIfEmpty()
from sm in ctx.SupplierManufacturerRelations
.Where(sm => sm.SupplierPNID == sp.SupplierPNID)
.DefaultIfEmpty()
from mp in ctx.ManufacturerParts
.Where(mp => mp.MfgPNID.Equals(sm.MfgPNID))
.DefaultIfEmpty()
from m in ctx.Manufacturers
.Where(m => m.ManufacturerID.Equals(mp.ManufacturerID))
.DefaultIfEmpty()
from im in ctx.ItemMasters
.Where(im => im.PreID == mp.PreID)
.Where(im => im.PartNumber == mp.PartNumber)
.DefaultIfEmpty()
from c in ctx.ComponentClasses
.Where(c => c.CCID == im.CCID)
.DefaultIfEmpty()
from um in ctx.UnitsOfMeasures
.Where(um => um.UOMID == sp.UOMID)
.DefaultIfEmpty()
select new
{ my variables}
var querylist = sqlQuery.Where(n => n.SupplierID == thisSupplier).ToList();
I also tried
from s in ctx.Suppliers
join sp in ctx.SupplierParts on s.SupplierID equals sp.SupplierID
join sm in ctx.SupplierManufacturerRelations on sp.SupplierPNID equals sm.SupplierPNID into spartgroup
from sm in spartgroup.DefaultIfEmpty()
join mp in ctx.ManufacturerParts on sm.MfgPNID equals mp.MfgPNID into mpartgroup
from mp in mpartgroup.DefaultIfEmpty()
join m in ctx.Manufacturers on mp.ManufacturerID equals m.ManufacturerID into mgroup
from m in mgroup.DefaultIfEmpty()
join im in ctx.ItemMasters
on new { key1 = (int)mp.PreID, key2 = (int)mp.PartNumber }
equals new { key1 = im.PreID, key2 = im.PartNumber }
into tpartgroup
from im in tpartgroup.DefaultIfEmpty()
join c in ctx.ComponentClasses on im.CCID equals c.CCID into fullgroup
from c in fullgroup.DefaultIfEmpty()
join um in ctx.UnitsOfMeasures on sp.UOMID equals um.UOMID
This SQL query works and doesn't omit the rows
SELECT Supplier.SupplierID
, SupplierPart.SupplierPNID
, SupplierPart.SupplierPN
, SupplierPart.Description
, SupplierManufacturerRelation.MfgPNID
, ManufacturerPart.PreID
, ManufacturerPart.PartNumber
, ItemMaster.CCID
, ItemMaster.Description AS Expr1
FROM Supplier
Inner JOIN SupplierPart
ON Supplier.SupplierID = SupplierPart.SupplierID
Left JOIN SupplierManufacturerRelation
ON SupplierPart.SupplierPNID = SupplierManufacturerRelation.SupplierPNID
Left JOIN ManufacturerPart
ON SupplierManufacturerRelation.MfgPNID = ManufacturerPart.MfgPNID
Left JOIN ItemMaster
ON ManufacturerPart.PreID = ItemMaster.PreID
AND ManufacturerPart.PartNumber = ItemMaster.PartNumber
WHERE Supplier.SupplierID = 9
For translating SQL to LINQ query comprehension:
Translate FROM subselects as separately declared variables.
Translate each clause in LINQ clause order, translating monadic operators (DISTINCT, TOP, etc) into functions applied to the whole LINQ query.
Use table aliases as range variables. Use column aliases as anonymous type field names.
Use anonymous types (new { ... }) for multiple columns.
Left Join is simulated by using a into join_variable and doing another from from the join variable followed by .DefaultIfEmpty().
Replace COALESCE with the conditional operator and a null test.
Translate IN to .Contains() and NOT IN to !...Contains()
SELECT * must be replaced with select range_variable or for joins, an anonymous object containing all the range variables.
SELECT fields must be replaced with select new { ... } creating an anonymous object with all the desired fields or expressions.
Proper FULL OUTER JOIN must be handled with an extension method.
So from your SQL, your query should look like:
var ans = from s in ctx.Suppliers
join sp in ctx.SupplierParts on s.SupplierID equals sp.SupplierID
join sm in ctx.SupplierManufacturerRelations on sp.SupplierPNID equals sm.SupplierPNID into smj
from sm in smj.DefaultIfEmpty()
join mp in ctx.ManufacturerParts on sm?.MfgPNID equals mp.MfgPNID into mpj
from mp in mpj.DefaultIfEmpty()
join im in ctx.ItemMasters on new { key1 = (int)mp.PreID, key2 = (int)mp.PartNumber } equals new { key1 = im.PreID, key2 = im.PartNumber } into imj
from im in imj.DefaultIfEmpty()
select new {
s.SupplierID, sp.SupplierPNID, sp.SupplierPN, sp.Description, sm.MfgPNID, mp.PreID, mp.PartNumber, im.CCID, Expr1 = im.Description
};
I want to convert this SQL code to LINQ. Here is my SQL code:
SELECT Rooms.RoomName AS RoomNo, Beds.BedName AS Beds, Rooms.RoomType, ISNULL(CheckIn.CheckIntatus,'') AS Status
FROM CheckIn
INNER JOIN GuestBeds ON CheckIn.GuestBedId = GuestBeds.Id
AND (CheckIn.CheckInStatus = 1 OR CheckIn.CheckIntatus = 2 OR CheckIn.CheckSIntatus = 3)
RIGHT JOIN Beds ON GuestBeds.BedId = Beds.Id
INNER JOIN Rooms ON Beds.RoomId = Rooms.Id
LEFT JOIN Guests ON CheckIn.GuestId = Guests.Id
WHERE Beds.Active = 1 AND Rooms.Active = 1
ORDER BY RoomName, Beds
It works well which means it shows all the RoomName with CheckInStatus. If the Room is not presence in CheckIn table, ot will return the status as Null.
So I want to convert the code to LINQ. SO here is my LINQ code:
from b in Beds
join w in Rooms on b.RoomsId equals w.Id
where (a.CheckInStatus == 3 || a.CheckInStatus == 1 || a.CheckInStatus == 2)
join p in GuestBeds on b.Id equals p.BedId
join a in CheckIn on p.Id equals a.GuestBedId
join t in Guests on a.GuestId equals t.Id
where b.Active == true && w.Active == true
orderby w.RoomName
select new
{
RoomName = w.RoomName,
BedName = b.BedName,
Status = a.CheckInStatus
}
It didnt worked like the first code. It only show the data which contain CheckInStatus. I want it to show all the RoomName inside Room database
Normally I would post some rules for converting SQL to LINQ but this is complicated enough I think I'd need to make new rules. I commented out the references to Guests because as a LEFT JOIN it has no bearing on the answer.
Pull out the WHERE on individual tables and make them sub-queries:
var ActiveBeds = Beds.Where(b => b.Active == 1);
var ActiveRooms = Rooms.Where(r => r.Active == 1);
In LINQ, a RIGHT JOIN must be done by flipping the join to be a left join, so we will create the two sides as sub-queries.
Left side of RIGHT JOIN:
Translate the JOIN conditions that aren't part of an equi-join into a LINQ where clause on the appropriate tables (alternately this could be a subquery as above). The LEFT JOIN becomes a LINQ join/from ... DefaultIfEmpty() phrase, but as noted above isn't needed.
var CheckInsGuestBedsGuests = from c in CheckIn
where (c.CheckInStatus == 1 || c.CheckInStatus == 2 || c.CheckInStatus == 3)
join gb in GuestBeds on c.GuestBedId equals gb.Id
//join g in Guests on c.GuestId equals g.Id into gj
//from g in gj.DefaultIfEmpty()
select new { c, gb /*, g */ };
Right side of RIGHT JOIN:
The other side of the RIGHT JOIN includes an INNER JOIN so put them together in a sub-query:
var ActiveBedsRooms = from b in ActiveBeds
join r in ActiveRooms on b.RoomId equals r.Id
select new { b, r };
Finally, flip the sub-queries to create a left join using the same idiom as above:
var ans = from br in ActiveBedsRooms
join cgbg in CheckInsGuestBedsGuests on br.b.Id equals cgbg.gb.BedId into cgbgj
from cgbg in cgbgj.DefaultIfEmpty()
select new {
RoomNo = br.r.RoomName,
Beds = br.b.BedName,
br.r.RoomType,
Status = cgbg.c.CheckInStatus
};
NOTE: If you were not using LINQ to SQL, the Status expression would fail when cgbg is null and you would need
Status = cgbg?.c.CheckInStatus
but unfortunately LINQ to SQL/EF doesn't handle the null conditional operators yet.
BTW, nice query - brings back memories of when I used to write hotel front desk software :)
I'm sorry for telling that I've a little bit weak on LINQ, I always do write SQL query before I start working on the complicated LINQ.
I want to ask that how to convert this SQL Query into LINQ with LEFT JOIN with multiple ON conditons with the OR operator.,
m.MerchandiseId will be use for twice in ON condition
SELECT
*
FROM
Inbox AS i
INNER JOIN [User] AS u ON i.FromUserId = u.UserId
LEFT OUTER JOIN Merchandise AS m ON
u.MerchandiseId = m.MerchandiseId
OR
i.ToMerchantId = m.MerchandiseId
WHERE
i.ToCompanyId = 10
OR
i.FromCompanyId = 10
var message = (from i in db.Inbox
join u in db.User on i.FromUserId equals u.UserId
join m in db.Merchandise on u.MerchandiseId equals m.MerchandiseId //here I want to ON i.MerchantId = m.MerchandiseId, but it doesn't allow
where i.ToCompanyId == user.CompanyId || i.FromCompanyId == user.CompanyId
orderby i.CreatedAt descending
group m.MerchandiseId by new { m.MerchandiseId, m.MerchandiseName } into grp
select new
{
MerchandiseId = grp.Key.MerchandiseId,
MerchandiseName = grp.Key.MerchandiseName,
InboxMessage = (from e in db.Inbox
join us in db.User on e.FromUserId equals us.UserId
join mer in db.Merchandise on us.MerchandiseId equals mer.MerchandiseId
where mer.MerchandiseId == grp.Key.MerchandiseId
orderby e.CreatedAt descending
select e.InboxMessage).FirstOrDefault(),
CreatedAt = (from e in db.Inbox
join us in db.User on e.FromUserId equals us.UserId
join mer in db.Merchandise on us.MerchandiseId equals mer.MerchandiseId
where mer.MerchandiseId == grp.Key.MerchandiseId
orderby e.CreatedAt descending
select e.CreatedAt).FirstOrDefault(),
}).ToList();
The bottom LINQ Query I've write for it. However, I just can work on the left join with multiple ON clause in LINQ. Appreciate if someone would help me on this. Thanks!
I don't believe Linq supports the use of the OR operator with multiple columns, but that said, I wouldn't use OR even in SQL as it makes the join's intention unclear and it also obscures where the data originated from - it also isn't immediately clear what happens if there are multiple matches for each column. Instead I would JOIN twice on the different columns and let the projection-clause handle it:
SELECT
*
FROM
Inbox
INNER JOIN [User] AS u ON i.FromUserId = u.UserId
LEFT OUTER JOIN Merchandise AS userMerchant ON u.MerchandiseId = userMerchant.MerchandiseId
LEFT OUTER JOIN Merchandise AS inboxMerchant ON Inbox.ToMerchantId = inboxMerchant .MerchandizeId
WHERE
Inbox.ToCompanyId = 10
OR
Inbox.FromCompanyId = 10
This can then be translated into Linq using the LEFT OUTER JOIN approach ( How to implement left join in JOIN Extension method )
Note that if you're using Entity Framework then you don't need to worry about doing any of this at all! Just use Include:
var query = db.Inbox
.Include( i => i.User )
.Include( i => i.User.Merchandise )
.Include i => i.Merchandise )
.Where( i => i.ToCompanyId = 10 || i.FromCompanyId == 10 );