left join failing in query with multiple joins - c#

Before this gets called a duplicate, I have looked around on SO and found a way to do this but it isn't working.
My query is
var GetAllProjects = from f in dc.vw_gmi_all_projects
join mc in dc.gmi_maintenance_classes on f.maintenance_classID equals mc.maintenance_classID
join ms in dc.gmi_maintenance_subclasses on f.maintenance_subclassID equals ms.maintenance_subclassID
join pm in dc.master_project_milestones on f.pmID equals pm.pmID
join ac in dc.vw_master_Countries on f.country_display_name equals ac.country_display_name
join pd in dc.gmi_project_details on f.project_dataID equals pd.project_dataID
join md in dc.vw2_master_districts on f.country_display_name equals md.element_display_name
join ml in dc.vw2_master_lmus on pd.dataID equals ml.elementID into gl from sub in gl.DefaultIfEmpty()
where (mc.maintenance_classID == 3 && ms.maintenance_subclassID != 11)
select new
{
f.project_dataID,
f.projectID,
f.project_title,
f.local_projectID,
f.pm_display_name,
f.reu_name,
f.reuID,
f.sectorID,
f.sector_display_name,
f.country_display_name,
f.maintenance_classID,
f.maintenance_subclassID,
mc.maintenance_class_display_name,
ms.maintenance_subclass_display_name,
pm.pm_name,
ac.region_display_name,
pd.dataID,
md.element_display_name,
ac.cluster_display_name,
display_name = sub.element_display_name
};
foreach (var a in GetAllProjects)
{
lst.Add(new ReportFilter
{
project_dataID = (int)a.project_dataID,
projectID = a.projectID,
project_title = a.project_title,
local_projectID = a.local_projectID,
pm_display_name = a.pm_display_name,
reu_name = a.reu_name,
reuID = a.reuID,
country_display_name = a.country_display_name,
sectorID = a.sectorID,
sector_display_name = a.sector_display_name,
maintenance_classID = a.maintenance_classID,
maintenance_subclassID = a.maintenance_subclassID,
maintenance_class_display_name = a.maintenance_class_display_name,
maintenance_subclass_display = a.maintenance_subclass_display_name,
pm_name = a.pm_name,
region_display_name = a.region_display_name,
dataID = a.dataID,
district = a.element_display_name,
cluster_display_name = a.cluster_display_name
});
}
This is where the left join is supposed to take place..
join ml in dc.vw2_master_lmus on pd.dataID equals ml.elementID into gl from sub in gl.DefaultIfEmpty()
This query runs fine if I leave out the attempt at left join and leave that join out entirely, but I need to get the left join to work so I can get the rest of the records. A typical join won't work like the others because it doesn't return any records.
So where am I going wrong with my query, or doing wrong? I know where its going wrong just not sure how to fix it.
Thanks
More Details
This query creates a list and then I query against this list. However, this query won't return any records because of my attempt at creating a left join.
EDIT
Here is the SQL that I wrote and trying to recreate it using Linq
select * from [vw_gmi_all_projects] f
inner join [gmi_maintenance_classes] mc on f.maintenance_classID = mc.maintenance_classID
inner join [gmi_maintenance_subclasses] ms on f.maintenance_subclassID = ms.maintenance_subclassID
inner join [master_project_milestones] pm on f.pmID = pm.pmID
inner join [vw_master_Countries] ac on f.country_display_name = ac.country_display_name
inner join [gmi_project_details] pd on f.project_dataID = pd.project_dataID
inner join [vw2_master_district] md on f.country_display_name = md.element_display_name
left join [vw2_master_lmu] ml on pd.dataID = ml.elementID
where (mc.maintenance_classID = 3 and ms.maintenance_subclassID != 11)
I hope this helps.

Try the syntax from this answer instead: https://stackoverflow.com/a/4739738/1869660
I think that's an easier syntax when creating LEFT JOINs (note: from, not join).
var GetAllProjects = from f in dc.vw_gmi_all_projects
join mc in dc...
join ms in dc...
join pm in dc...
join ac in dc...
join pd in dc...
join md in dc...
from ml in dc.vw2_master_lmus.Where(ml => ml.elementID == pd.dataID).DefaultIfEmpty()
where ...

Related

Complex joins in Linq with multiple tables and LEFT OUTER JOIN

Hoping someone can point me in the right direction with this join. I'm trying to convert some SQL to Linq. My SQL has a left outer join after several inner joins. The following SQL produces the desired result:
SELECT TOP(50) [t].[TagFriendlyName] AS [TagName], [t0].[timeStamp] AS [LastSeen], [l].[Name] AS [LocationName]
FROM [Tags] AS [t]
INNER JOIN [tag_reads] AS [t0] ON [t].[epc] = [t0].[epc]
INNER JOIN [ReaderData] AS [r] ON [t0].[ReaderDataId] = [r].[Id]
LEFT OUTER JOIN [Readers] AS [r0] ON [r].[mac_address] = [r0].[mac_address]
INNER JOIN [Locations] AS [l] on [t0].[antennaPort] = [l].[AntennaId] AND [r].[Id] = [l].[ReaderId]
GROUP BY [t].[TagFriendlyName], [t0].[timeStamp], [l].[Name]
ORDER BY [t0].[timeStamp] DESC
My Linq code is as follows, but I can't figure out how to get the left outer join inserted properly. Not sure how to introduce the Readers table that needs the LEFT OUTER JOIN:
var query = (
from tags in db.Tags
join tagreads in db.tag_reads on tags.epc equals tagreads.epc
join readerdata in db.ReaderData on tagreads.ReaderDataId equals readerdata.Id
join readers in db.Readers on readerdata.mac_address equals readers.mac_address
group tags by new { tags.TagFriendlyName, timestamp = tagreads.timeStamp, readerdata.mac_address } into grp
select new CurrentStatus()
{
TagName = grp.Key.TagFriendlyName,
LastSeen = grp.Key.timestamp,
LocationName = grp.Key.mac_address
}
)
.OrderByDescending(o => o.LastSeen)
According to the documentation I need to use DefaultIfEmpty(), but I'm not sure where to introduce the Readers table.
Using EF Core 3.1.0. THANKS!
You should apply Left Join this way:
join readers in db.Readers on readerdata.mac_address equals readers.mac_address into readersJ
from readers in readersJ.DefaultIfEmpty()
The full code:
var query = (
from tags in db.Tags
join tagreads in db.tag_reads on tags.epc equals tagreads.epc
join readerdata in db.ReaderData on tagreads.ReaderDataId equals readerdata.Id
join readers in db.Readers on readerdata.mac_address equals readers.mac_address into readersJ
from readers in readersJ.DefaultIfEmpty()
join locations in db.Locations
on new { ap = tagreads.antennaPort, rd = readerdata.Id }
equals new { ap = locations.AntennaId, rd = locations.ReaderId }
group tags by new { tags.TagFriendlyName, timestamp = tagreads.timeStamp, readerdata.mac_address } into grp
select new CurrentStatus()
{
TagName = grp.Key.TagFriendlyName,
LastSeen = grp.Key.timestamp,
LocationName = grp.Key.mac_address
}
)
.OrderByDescending(o => o.LastSeen)

LINQ giving difference results to SQL

I have been tasked with replacing SQL query with a LINQ query and in the main I get the data that I would expect, however I think there is a join that has gone wrong somewhere and I'm not sure how or where as most of the time I avoid EF where I can in favour of dapper.
The SQL I have been given
SELECT
SFM.FieldId,
QSRA.Answer,
SFM.FieldNo
FROM
[forms].QS
INNER JOIN
[sessions].QSR
ON QSR.QSNo = QS.QSNo
INNER JOIN
(
SELECT
MAX(QS.QSVersion) AS LatestVersion
FROM
[forms].QS
INNER JOIN
[sessions].QSR
ON QSR.QSNo = QS.QSNo
WHERE
QSR.QsrId = #QSRID
AND QS.StatusId = 2
) AS QSLatestVer
ON QS.QSVersion = QSLatestVer.LatestVersion
INNER JOIN
[forms].QSSectionMappings QSM
ON QSM.QSId = QS.QSId
INNER JOIN
[forms].SectionFieldMappings SFM
ON SFM.SectionId = QSM.SectionId
INNER JOIN
[sessions].QSRAnswers QSRA
ON (
QSRA.QsrId = QSR.QsrId
AND QSRA.FieldNo = SFM.FieldNo
)
WHERE
QSR.QsrId = #QSRID;
The LINQ I have used to replace it with and then am going to look at refining.
var results = (from qs in QS
join qsr in QSRs on qs.QSNo equals qsr.QSNo
join qsm in QSSectionMappings on qs.QSId equals qsm.QSId
join sfm in SectionFieldMappings on qsm.SectionId equals sfm.SectionId
join qsra in QSRAnswers on qsr.QsrId equals qsra.QsrId
join sub in (from subQs in QS
join subQsr in QSRs on subQs.QSNo equals subQsr.QSNo
where subQs.StatusId == 2 && subQsr.QsrId == Guid.Parse(qsrIdGuid)
select subQs.QSVersion
) on qs.QSVersion equals sub
where qsr.QsrId == Guid.Parse(qsrIdGuid)
group new
{
FieldId = sfm.FieldId,
Answer = qsra.Answer,
FieldNo = decimal.Parse(sfm.FieldNo),
} by new
{
FieldId = sfm.FieldId,
Answer = qsra.Answer,
FieldNo = sfm.FieldNo
} into g
select new
{
FieldId = g.Key.FieldId,
Answer = g.Key.Answer,
FieldNo = g.Key.FieldNo,
}
);
The results I get with the SQL are
FieldId |Answer |FieldNo
40D10975-AF2E-4518-AC35-08D7C70E1BF9 |3/17/2020 12:00:00 AM |1
71A95FD5-08E0-4201-AC36-08D7C70E1BF9 |3/25/2020 12:00:00 AM |2
The results I get with LINQ are
FieldId |Answer |FieldNo
40d10975-af2e-4518-ac35-08d7c70e1bf9 |3/17/2020 12:00:00 AM |1
--Correct
40d10975-af2e-4518-ac35-08d7c70e1bf9 |3/25/2020 12:00:00 AM |1 --Wrong
71a95fd5-08e0-4201-ac36-08d7c70e1bf9 |3/17/2020 12:00:00 AM |2 --Wrong
71a95fd5-08e0-4201-ac36-08d7c70e1bf9 |3/25/2020 12:00:00 AM |2
--Correct
I would appreciate if you could let me know where I am going wrong in the join
The results are the same with the nested select, and the grouping as without the grouping.
In the SQL you have the following for joining the QSRAnwers table
INNER JOIN
[sessions].QSRAnswers QSRA
ON (
QSRA.QsrId = QSR.QsrId
AND QSRA.FieldNo = SFM.FieldNo
)
However in the Linq code you have
join qsra in QSRAnswers on qsr.QsrId equals qsra.QsrId
So you're missing the FieldNo comparison for that join. Just change it to
join qsra in QSRAnswers
on new{qsr.QsrId, sfm.FieldNo} equals new{qsra.QsrId, qsra.FieldNo}
To get the same functionality.

Making 1 table join on 2 other tables with LINQ

I'm trying to join 1 table onto 2 other tables with LINQ, but I can't seem to figure out how this is done.
I can make it work writing pure SQL statements within Visual studio, I'm just not sure how to convert this into LINQ.
Here's my SQL statement:
SELECT c.CustomerId, c.CustomerName, pw.Number, pc.Number FROM Customers as c
LEFT JOIN Tasks as k ON k.Id = c.Task_Id
LEFT JOIN Workers as w ON w.Id = k.Worker_Id
LEFT JOIN PersonNumbers as pw ON pw.Person_Id = w.Id
LEFT JOIN Chiefs as ch ON ch.Id = k.Chief_Id
LEFT JOIN PersonNumbers as pc ON pc.Person_Id = ch.Id
Perhaps this requires a bit of explanation.
We have a bunch of Customers and these customers can have some tasks. Within a task you will have workers and chiefs. Within the PersonNumbers table, I have some extra information about workers and chiefs, and this is the information that I need.
You should be able to do something like the following assuming all your joins are based on foreign keys that should result in navigation properties in your entities. The DefaultIfEmpty is what makes everything a left join.
var results = from c in db.Customers
from k in c.Tasks.DefaultIfEmpty()
from w in k.Workers.DefaultIfEmpty()
from pw in w.Persons.DefaultIfEmpty()
from ch in k.Chiefs.DefaultIfEmpty()
from pc in ch.Persons.DefaultIfEmpty()
select new
{
c.CustomerId,
c.CustomerName,
pw.Number,
pc.Number
};
If you don't have the navigation properties then you'll have to use join.
var results = from c in db.Customers
join xk in db.Tasks on xk.Id equals c.Task_Id
from k in xk.DefaultIfEmpty()
join xw in db.Workers on xw.Id equals k.Worker_Id
from w in xw.DefaultIfEmpty()
join xpw in db.Persons on xpw.Person_Id equals w.Id
from pw in xpw.DefaultIfEmpty()
join xch in db.Chiefs on xch.Id equals k.Chief_Id
from ch in xch.DefaultIfEmpty()
join xpc in db.Persons on xpc.Person_Id euals ch.Id
from pc in xpc.DefaultIfEmpty()
select new
{
c.CustomerId,
c.CustomerName,
pw.Number,
pc.Number
};

translate postgres sql to linq. Is it possible?

I need translate next sql to linq is it possible? That will have approximately the same speed
SELECT Count(tblcollectionimage.lngimageid),
tblcollectiontree.lngcollectionid,
tblcollection.txtname
FROM (tblcollectiontree
LEFT JOIN tblcollectionimage
ON blcollectiontree.lngcollectionid =
tblcollectionimage.lngcollectionid)
JOIN tblcollection
ON tblcollectiontree.lngcollectionid = tblcollection.lngcollectionid
WHERE lngcollectionparentid = 0
GROUP BY tblcollectiontree.lngcollectionid,
tblcollection.txtname
I have currently such linq but it doesn't work.
var results =(from collection in dataBase.tblcollections
join collectionTree in dataBase.tblcollectiontrees on
collection.lngcollectionid equals collectionTree.lngcollectionid
into generalCollections
from generalCollection in generalCollections
join images in dataBase.tblcollectionimages on
collection.lngcollectionid equals images.lngcollectionid
into generalCollectionImages
from generalCollectionImage in
generalCollectionImages.DefaultIfEmpty()
group generalCollectionImage by
generalCollectionImage.lngcollectionid into hello
from hellos in hello.DefaultIfEmpty()
join collection in dataBase.tblcollections on
hello.Key equals collection.lngcollectionid
select new
{
id = hello.Key,
name = hello.Count()
}).ToList();

multiple left joins with Linq

I'm struggling with a Linq query involving left joins. Here is the SQL I'm trying to convert:
SELECT EmailStats.EmailAddress, EmailVoting.DateAdded, EmailVoting.ResponseText,
EmailStats.DateSent, EmailAlerts.Name, UserDetails.EmployeeNumber
FROM EmailAlerts
INNER JOIN EmailStats
ON EmailAlerts.EmailAlertID = EmailStats.EmailAlertID
INNER JOIN UserDetails
ON EmailStats.UserId = UserDetails.UserId
LEFT OUTER JOIN EmailVoting
ON EmailAlerts.EmailAlertID = EmailVoting.EmailAlertID
AND EmailVoting.UserId = EmailStats.UserId
Where EmailAlerts.EmailAlertID = 43 AND EmailStats.IsTestSend = 0
The SQL is returning the correct data with the EmailVoting fields null if the rows don't exist. Below is the LINQ I currently have:
from ea in db.EmailAlerts
join es in db.EmailStats on ea.EmailAlertID equals es.EmailAlertID
join ud in db.UserDetails on es.UserId equals ud.UserId
join ev in db.EmailVoting on ea.EmailAlertID equals ev.EmailAlertID into vm
from v in vm.DefaultIfEmpty()
join ev in db.EmailVoting on es.UserId equals ev.UserId into udItems
from u in udItems.DefaultIfEmpty()
where v.EmailAlertID == emailAlertID
What I thought was the same in LINQ, isn't displaying correctly and is in fact displaying an entry with a different EmailAlertID. Anyone see where I might be going wrong?
Thanks
Try this:
from ea in db.EmailAlerts
join es in db.EmailStats on ea.EmailAlertID equals es.EmailAlertID
join ud in db.UserDetails on es.UserId equals ud.UserId
join ev in db.EmailVoting on new {ev.EmailAlertID, ev.UserId} equals new {ea.EmailAlertID, es.UserId} into vm
from v in vm.DefaultIfEmpty()
where v.EmailAlertID == emailAlertID

Categories

Resources