Many Joins with single conditions VS Fewer Joins with multiple conditions - performance

I found a query on the back end of a report that has multiple joins to multiple tables and thought that I'd improve the performance of the query by reducing the number of joins and consolidating the conditions on those joins. My theory being that every join would force the query to go over that table againso, fewer joins equal fewer reads.
What I found:
SELECT /* person fields, address fields, contact fields */
FROM PositionHistory ph
LEFT JOIN Person p ON p.ID = p.ID AND ph.Active = 'Y'
LEFT JOIN PersonAddress pa1 ON pa1.AddrID = p.ID AND pa1.Type = 'Current'
LEFT JOIN Address a1 ON a1.ID = pa1.AddrID
LEFT JOIN PersonPhone pp1_p ON pp1_p.ID = pa1.PhoneID AND pp1_p.Type = 'Phone'
LEFT JOIN Phone p1_p ON p1_p = pp1_p.ID
LEFT JOIN PersonPhone pp1_c ON pp1_c.ID = pa1.PhoneID AND pp1_c.Type = 'Cell'
LEFT JOIN Phone p1_c ON p1_c = pp1_c.ID
LEFT JOIN PersonAddress pa2 ON pa2.AddrID = p.ID AND pa2.Type = 'Work'
LEFT JOIN Address a2 ON a2.ID = pa2.AddrID
LEFT JOIN PersonPhone pp2_p ON pp2_p.ID = pa2.PhoneID AND pp2_p.Type = 'Phone'
LEFT JOIN Phone p2_p ON p2_p = pp2_p.ID
LEFT JOIN PersonPhone pp2_c ON pp2_c.ID = pa2.PhoneID AND pp2_c.Type = 'Cell'
LEFT JOIN Phone p2_c ON p2_c = pp2_c.ID
LEFT JOIN OtherContact oc1 ON oc1.PersonID = p.ID AND oc1.Type = 'Work'
LEFT JOIN OtherContact oc2 ON oc2.PersonID = p.ID AND oc2.Type = 'Home'
All this in an effort to get the data onto one row for presentation in an SSRS report.
I found that by using a matrix in the report I could get the data onto one row so that wasn't an issue anymore but the report was slow to run and I thought I'd work on the query to improve performance. What I tried:
SELECT /* person fields, address fields, contact fields */
FROM PositionHistory ph
LEFT JOIN Person p ON p.ID = p.ID AND ph.Active = 'Y'
LEFT JOIN PersonAddress pa ON pa.AddrID = p.ID AND pa.Type IN ('Current','Work')
LEFT JOIN Address a ON a.ID = pa.AddrID
LEFT JOIN PersonPhone pp ON pp.ID = pa.PhoneID AND pp.Type IN ('Phone', 'Cell')
LEFT JOIN Phone p ON p = pp.ID
LEFT JOIN OtherContact oc ON oc.PersonID = p.ID AND oc.Type IN ('Work', 'Home')
The number of rows returned increased by about 5 times, which makes sense. But when I used SET STATISTICS IO ON; I found that the number of overall logical reads increased from 22.7k to 37.3k which is the opposite of what I expected. At the very least I'd think it would be the same. Wouldn't the query be traversing the data fewer times with fewer joins? I need to understand what's going on here so that I can develop well-performing queries going forward but the original query isn't something I would've ever thought to do!
I might as well use the original query for the report at this point since it (apparently, somehow, bafflingly) performs better and then the report itself doesn't have to process the data onto one row. Any thoughts on how to improve would be welcome! I'm sure I'm missing something basic but I haven't found anything online or as I've tweaked the query.

Related

sql select records that don't have relation in a third table

I have three tables
CLAIMS_TB
CLAIMS_RISK_TB
VEHICLE_TB
And then I need this result below:
Who can help me or share with me the query to be used?
N.B: If the code is 700 it means that it is a vehicle and it must fill the column called "ai_vehicle_use" otherwise it must leave it blank because "VEHICLE_TB" table contains only vehicles
This is what I tried:
select
klm.CM_PL_INDEX,
klm.cm_no,
klmrisk.cr_risk_code,
CASE WHEN klm.CM_PR_CODE = '0700' THEN klmrisk.cr_risk_code ELSE '' END,
veh.ai_vehicle_use
from CLAIMS_TB klm
JOIN CLAIMS_RISK_TB klmrisk
ON (klm.cm_index = klmrisk.cr_cm_index)
INNER JOIN VEHICLE_TB veh
on veh.ai_regn_no = klm.cm_no
where klm.cm_no='CL/01/044/00001/01/2018'
or klmrisk.cr_cm_index='86594'
order by klmrisk.cr_risk_code;
I believe this could fit your needs.
SELECT
*
FROM CLAIMS_TB AS c
LEFT JOIN CLAIMS_RISK_TB cl ON c.cm_index = cl.cr_cm_index
LEFT JOIN VEHICLE_TB v ON cl.cr_risk_code = v.ai_risk_index
Finaly I find the solution, query below works:
select * from CLAIMS_TB c
JOIN CLAIMS_RISK_TB cr ON( C.CM_INDEX = cr.cr_cm_index)
LEFT OUTER JOIN VEHICLE_TB v ON (cr.cr_risk_code = v.ai_regn_no);

INNER Join ORACLE [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I am having a issue with my INNER join for Oracle. I was hoping you guys could review. Basically the Address table houses addresses of incident and person. I basically need to do a SELECT statement for the incident address then an INNER join to get the persons address as well, for some reason it is not working.
select DISTINCT INCIDENT_PEOPLE_VW.INC_REPORT_NUMBER,trunc((to_number(to_char (offense_status_date,'yyyymmdd'))-to_number(to_char(DOB,'yyyymmdd')))/10000) as AGE,
INCIDENT_PEOPLE_VW.INCIDENT_ID,
OFFENSES.OFFENSE_STATUS_DATE,
INCIDENT_PEOPLE_VW.AGNCY_CD_AGENCY_CODE,
INCIDENT_PEOPLE_VW.STATUS,
INCIDENT_PEOPLE_VW.SEX_SEX_CODE,
INCIDENT_PEOPLE_VW.RACE_RACE_CODE,
INCIDENT_PEOPLE_VW.LNAME,
INCIDENT_PEOPLE_VW.FNAME,
INCIDENT_PEOPLE_VW.DOB,
OFFENSES.REMARKS,
OFFENSE_CODES.OFFENSE_DESC,
P.LONGITUDE,P.LATITUDE,
suspicion_Codes.DESCRIPTION,
P.STREET_NUMBER, P.STREET_NAME,P.STREET_CD_STREET_TYPE_CODE,P.CITY,P.STATE_CD_STATE_CODE,P.ZIP5,
AH.STREET_NUMBER, AH.STREET_NAME,AH.STREET_CD_STREET_TYPE_CODE,AH.CITY,AH.STATE_CD_STATE_CODE,AH.ZIP5
from
INCIDENT_PEOPLE_VW,
OFFICER_INCIDENTS,OFFENSES,OFFENSE_CODES, OFFICERS,
ADDRESSES P, INCIDENT_ADDRESSES,offender_suspicions,suspicion_Codes,person_addresses D
INNER JOIN ADDRESSES ah
ON D.address_id= ah.address_ID
where OFFICER_INCIDENTS.INC_INCIDENT_ID=INCIDENT_PEOPLE_VW.INCIDENT_ID
AND INCIDENT_PEOPLE_VW.INCIDENT_ID=INCIDENT_ADDRESSES.INCIDENT_ID
AND INCIDENT_ADDRESSES.ADDRESS_ID=P.ADDRESS_ID
AND INCIDENT_PEOPLE_VW.INCIDENT_ID = OFFENSES.INC_INCIDENT_ID
AND INCIDENT_PEOPLE_VW.INCIDENT_ID = OFFENDER_SUSPICIONS.OFFNSE_INC_INCIDENT_ID
AND OFFENDER_SUSPICIONS.SUSPICN_CD_SUSPICION_CODE =SUSPICION_CODES.SUSPICION_CODE
AND OFFENSES.OFFNS_CD_OFFENSE_CODE = OFFENSE_CODES.OFFENSE_CODE
AND OFFICER_INCIDENTS.OFF1_OFFICER_ID = OFFICERS.OFFICER_ID
and OFFICER_INCIDENTS.ORC_ROLE_CODE='R'
and incident_people_vw.status='A'
and INCIDENT_PEOPLE_VW.ROLE_ROLE_TYPE IN ('A','S')
AND trunc((to_number(to_char(offense_status_date,'yyyymmdd'))-to_number(to_char (DOB,'yyyymmdd')))/10000) <= 17
The exact reason for your query to fail is the one #Politank-Z wrote you ...
You are mixing ANSI syntax of joins with the "old" syntax with join predicates in the WHERE clause.
If you used the ANSI syntax consistently, you would easily spot the problem, being that your person_addresses table join lacks the actual join predicate and, as such, your DB server is doing a cartesian product instead, effectively devouring all of your temporary tablespace.
Here you are, a slight iterative revamp of your query ...
Iteration 1 - introducing basic indentation and remaking all WHERE-clause join predicates to ANSI joins
select distinct
INCIDENT_PEOPLE_VW.INC_REPORT_NUMBER,
trunc((to_number(to_char(offense_status_date,'yyyymmdd')) - to_number(to_char(DOB,'yyyymmdd')))/10000) as AGE,
INCIDENT_PEOPLE_VW.INCIDENT_ID,
OFFENSES.OFFENSE_STATUS_DATE,
INCIDENT_PEOPLE_VW.AGNCY_CD_AGENCY_CODE,
INCIDENT_PEOPLE_VW.STATUS,
INCIDENT_PEOPLE_VW.SEX_SEX_CODE,
INCIDENT_PEOPLE_VW.RACE_RACE_CODE,
INCIDENT_PEOPLE_VW.LNAME,
INCIDENT_PEOPLE_VW.FNAME,
INCIDENT_PEOPLE_VW.DOB,
OFFENSES.REMARKS,
OFFENSE_CODES.OFFENSE_DESC,
P.LONGITUDE,P.LATITUDE,
suspicion_Codes.DESCRIPTION,
P.STREET_NUMBER, P.STREET_NAME,P.STREET_CD_STREET_TYPE_CODE,P.CITY,P.STATE_CD_STATE_CODE,P.ZIP5,
AH.STREET_NUMBER, AH.STREET_NAME,AH.STREET_CD_STREET_TYPE_CODE,AH.CITY,AH.STATE_CD_STATE_CODE,AH.ZIP5
from
INCIDENT_PEOPLE_VW
join OFFICER_INCIDENTS
on OFFICER_INCIDENTS.INC_INCIDENT_ID = INCIDENT_PEOPLE_VW.INCIDENT_ID
join OFFENSES
on OFFENSES.INC_INCIDENT_ID = INCIDENT_PEOPLE_VW.INCIDENT_ID
join OFFENSE_CODES
on OFFENSE_CODES.OFFENSE_CODE = OFFENSES.OFFNS_CD_OFFENSE_CODE
join OFFICERS
on OFFICERS.OFFICER_ID = OFFICER_INCIDENTS.OFF1_OFFICER_ID
join INCIDENT_ADDRESSES
on INCIDENT_ADDRESSES.INCIDENT_ID = INCIDENT_PEOPLE_VW.INCIDENT_ID
join ADDRESSES P
on P.ADDRESS_ID = INCIDENT_ADDRESSES.ADDRESS_ID
join offender_suspicions
on OFFENDER_SUSPICIONS.OFFNSE_INC_INCIDENT_ID = INCIDENT_PEOPLE_VW.INCIDENT_ID
join suspicion_Codes
on SUSPICION_CODES.SUSPICION_CODE = OFFENDER_SUSPICIONS.SUSPICN_CD_SUSPICION_CODE
person_addresses D,
INNER JOIN ADDRESSES ah
ON ah.address_ID = D.address_id
where
OFFICER_INCIDENTS.ORC_ROLE_CODE = 'R' and
incident_people_vw.status = 'A' and
INCIDENT_PEOPLE_VW.ROLE_ROLE_TYPE IN ('A','S') and
trunc((to_number(to_char(offense_status_date,'yyyymmdd'))-to_number(to_char(DOB,'yyyymmdd')))/10000) <= 17
;
Iteration 2 - identifying mistakenly forgotten cartesian products (i.e. joins without a join predicate, because when you put all your join predicates to your WHERE clause, you EASILY forget some)
In this case it's the PERSON_ADDRESSES table.
Iteration 3 - fixing the missing join predicates
select distinct
INCIDENT_PEOPLE_VW.INC_REPORT_NUMBER,
trunc((to_number(to_char(offense_status_date,'yyyymmdd')) - to_number(to_char(DOB,'yyyymmdd')))/10000) as AGE,
INCIDENT_PEOPLE_VW.INCIDENT_ID,
OFFENSES.OFFENSE_STATUS_DATE,
INCIDENT_PEOPLE_VW.AGNCY_CD_AGENCY_CODE,
INCIDENT_PEOPLE_VW.STATUS,
INCIDENT_PEOPLE_VW.SEX_SEX_CODE,
INCIDENT_PEOPLE_VW.RACE_RACE_CODE,
INCIDENT_PEOPLE_VW.LNAME,
INCIDENT_PEOPLE_VW.FNAME,
INCIDENT_PEOPLE_VW.DOB,
OFFENSES.REMARKS,
OFFENSE_CODES.OFFENSE_DESC,
P.LONGITUDE,P.LATITUDE,
suspicion_Codes.DESCRIPTION,
P.STREET_NUMBER, P.STREET_NAME,P.STREET_CD_STREET_TYPE_CODE,P.CITY,P.STATE_CD_STATE_CODE,P.ZIP5,
AH.STREET_NUMBER, AH.STREET_NAME,AH.STREET_CD_STREET_TYPE_CODE,AH.CITY,AH.STATE_CD_STATE_CODE,AH.ZIP5
from
INCIDENT_PEOPLE_VW
join OFFICER_INCIDENTS
on OFFICER_INCIDENTS.INC_INCIDENT_ID = INCIDENT_PEOPLE_VW.INCIDENT_ID
join OFFENSES
on OFFENSES.INC_INCIDENT_ID = INCIDENT_PEOPLE_VW.INCIDENT_ID
join OFFENSE_CODES
on OFFENSE_CODES.OFFENSE_CODE = OFFENSES.OFFNS_CD_OFFENSE_CODE
join OFFICERS
on OFFICERS.OFFICER_ID = OFFICER_INCIDENTS.OFF1_OFFICER_ID
join INCIDENT_ADDRESSES
on INCIDENT_ADDRESSES.INCIDENT_ID = INCIDENT_PEOPLE_VW.INCIDENT_ID
join ADDRESSES P
on P.ADDRESS_ID = INCIDENT_ADDRESSES.ADDRESS_ID
join offender_suspicions
on OFFENDER_SUSPICIONS.OFFNSE_INC_INCIDENT_ID = INCIDENT_PEOPLE_VW.INCIDENT_ID
join suspicion_Codes
on SUSPICION_CODES.SUSPICION_CODE = OFFENDER_SUSPICIONS.SUSPICN_CD_SUSPICION_CODE
join person_addresses D
on D.<some column> = <some table from the above ones>.<some column from the table>
JOIN ADDRESSES ah
ON AH.address_id = D.address_id
where
OFFICER_INCIDENTS.ORC_ROLE_CODE = 'R' and
incident_people_vw.status = 'A' and
INCIDENT_PEOPLE_VW.ROLE_ROLE_TYPE IN ('A','S') and
trunc((to_number(to_char(offense_status_date,'yyyymmdd'))-to_number(to_char(DOB,'yyyymmdd')))/10000) <= 17
;
Iteration 4 - fixing the wonderfully strange age calculation, introducing consistent formatting and consistent table aliases to improve readability
select distinct
IP.incident_id, IP.inc_report_number,
-- trunc((to_number(to_char(OFS.offense_status_date,'yyyymmdd')) - to_number(to_char(IP.dob,'yyyymmdd')))/10000) as age,
months_between(OFS.offense_status_date, IP.dob) / 12 as age
OFS.offense_status_date,
IP.agncy_cd_agency_code, IP.status, IP.sex_sex_code, IP.race_race_code, IP.lname, IP.fname, IP.dob,
OFS.remarks,
OC.offense_desc,
AIA.longitude, AIA.latitude,
SC.description,
AIA.street_number, AIA.street_name, AIA.street_cd_street_type_code, AIA.city, AIA.state_cd_state_code, AIA.zip5,
PAA.street_number, PAA.street_name, PAA.street_cd_street_type_code, PAA.city, PAA.state_cd_state_code, PAA.zip5
from
incident_people_vw IP
join officer_incidents OI
on OI.inc_incident_id = IP.incident_id
join offenses OFS
on OFS.inc_incident_id = IP.incident_id
join offense_codes OC
on OC.offense_code = OFS.offns_cd_offense_code
join officers O
on O.officer_id = OI.off1_officer_id
join incident_addresses IA
on IA.incident_id = IP.incident_id
join addresses AIA
on AIA.address_id = IA.address_id
join offender_suspicions OS
on OS.offnse_inc_incident_id = IP.incident_id
join suspicion_codes SC
on SC.suspicion_code = OS.suspicn_cd_suspicion_code
join person_addresses PA
on PA.<some column> = <some table alias from the above ones>.<some column from the table>
join addresses PAA
on PAA.address_id = PA.address_id
where
OI.orc_role_code = 'R' and
IP.status = 'A' and
IP.role_role_type in ('A','S') and
-- trunc((to_number(to_char(OFS.offense_status_date,'yyyymmdd')) - to_number(to_char(IP.dob,'yyyymmdd')))/10000) <= 17
months_between(OFS.offense_status_date, IP.dob) / 12 <= 17
;
Enjoy. And from now on always use the ANSI join syntax.

Linq left outer group by, then left outer the group

I've this query that i'm trying to put as linq:
select *
from stuff
inner join stuffowner so on so.stuffID = stuff.stuffID
left outer join (select min(loanId) as loanId, stuffownerId from loan
where userid = 1 and status <> 2 group by stuffownerId) t on t.stuffownerid = so.stuffownerid
left outer join loan on t.LoanId = loan.LoanId
when this is done, I would like to do a linq Group by to have Stuff has key, then stuffowners + Loan as value.
I can't seem to get to a nice query without sub query (hence the double left outer).
So basically what my query does, is for each stuff I've in my database, bring the owners, and then i want to bring the first loan a user has made on that stuff.
I've tried various linq:
from stuff in Stuffs
join so in StuffOwners on stuff.StuffId equals so.StuffId
join tLoan in Loans on so.StuffOwnerId equals tLoan.StuffOwnerId into tmpJoin
from tTmpJoin in tmpJoin.DefaultIfEmpty()
group tTmpJoin by new {stuff} into grouped
select new {grouped, fluk = (int?)grouped.Max(w=> w.Status )}
This is not good because if I don't get stuff owner and on top of that it seems to generate a lot of queries (LinqPad)
from stuff in Stuffs
join so in StuffOwners on stuff.StuffId equals so.StuffId
join tmpLoan in
(from tLoan in Loans group tLoan by tLoan.StuffOwnerId into g
select new {StuffOwnerId = g.Key, loanid = (from t2 in g select t2.LoanId).Max()})
on so.StuffOwnerId equals tmpLoan.StuffOwnerId
into tmptmp from tMaxLoan in tmptmp.DefaultIfEmpty()
select new {stuff, so, tmptmp}
Seems to generate a lot of subqueries as well.
I've tried the let keyworkd with:
from tstuffOwner in StuffOwners
let tloan = Loans.Where(p2 => tstuffOwner.StuffOwnerId == p2.StuffOwnerId).FirstOrDefault()
select new { qsdq = tstuffOwner, qsdsq= (int?) tloan.Status, kwk= (int?) tloan.UserId, kiwk= tloan.ReturnDate }
but the more info i get from tLoan, the longer the query gets with more subqueries
What would be the best way to achieve this?
Thanks

Oracle SQL developer how to display NULL value of the foreign key

I am going to try to use left outer join between Ticket and Membership.
However, it does not display the foreign key of NULL values on Ticket. Could you give me some answer for this what's wrong with this query?
Thanks.
FROM Ticket t, Production pro, Performance per, Price, Price_level, Booking, Customer, Customer_Concession ccons, Membership, Member_concession mcons
WHERE t.performanceid = per.performanceid AND
t.PRODUCTIONID = Price.PRODUCTIONID AND
t.levelId = Price.levelId AND
Price.PRODUCTIONID = pro.PRODUCTIONID AND
Price.levelId = Price_level.levelId AND
Booking.bookingId (+) = t.bookingId AND
Customer.customerId = Booking.customerId AND
ccons.cConcessionId (+) = Customer.cConcessionId AND
Membership.membershipId (+) = t.membershipId AND
Membership.mConcessionId = mcons.mConcessionId
ORDER BY t.ticketId
One potential problem you have is these two conditions:
Booking.bookingId (+) = t.bookingId AND
Customer.customerId = Booking.customerId AND
Since you're doing an outer join to Booking, its columns will appear as NULL when no match is found; but then your doing a normal join to Customer, so those rows will be eliminated since NULL cannot be equal to anything. You may want to change the second line to an outer join as well.
But, I don't know if that's your primary problem, since I don't actually understand exactly what you're asking. What do you mean by "NULL value of the foreign key"? You haven't specified what your foreign keys are.
To expand on Dave's observation, and to give you an example of SQL92 syntax, please please please learn it and get away from Oracle's own outer join syntax.
FROM
TICKET t
JOIN Performance per
ON per.performance_id = t.performance_id
JOIN Production pro
ON pro.produciton_id = t.production_id
JOIN PRICE pr
ON pr.production_id = pro.production_id
AND pr.levelId = t.level_id
JOIN price_level pl
ON pl.levelid = pr.levelid
LEFT OUTER JOIN booking b
on b.booking_id = t.booking_id
LEFT OUTER JOIN customer cus
on cus.customer_id = b.customer_id
LEFT OUTER JOIN customer_concession cons
ON cons.concession_id = cus.concession_id
LEFT OUTER JOIN memebership m
ON M.membership_id = t.membership_id
LEFT OUTER JOIN membership_concession mc
ON mc.mConcession_id = m.mConcession_id
Order by t.ticketid

LINQ nested joins

Im trying to convert a SQL join to LINQ. I need some help in getting the nested join working in LINQ.
This is my SQL query, Ive cut it short just to show the nested join in SQL:
select distinct
txtTaskStatus as TaskStatusDescription,
txtempfirstname+ ' ' + txtemplastname as RaisedByEmployeeName,
txtTaskPriorityDescription as TaskPriorityDescription,
dtmtaskcreated as itemDateTime,
dbo.tblTask.lngtaskid as TaskID,
dbo.tblTask.dtmtaskcreated as CreatedDateTime,
convert(varchar(512), dbo.tblTask.txttaskdescription) as ProblemStatement,
dbo.tblTask.lngtaskmessageid,
dbo.tblMessage.lngmessageid as MessageID,
case when isnull(dbo.tblMessage.txtmessagesubject,'') <> '' then txtmessagesubject else left(txtmessagedescription,50) end as MessageSubject,
dbo.tblMessage.txtmessagedescription as MessageDescription,
case when dbo.tblMessage.dtmmessagecreated is not null then dbo.tblMessage.dtmmessagecreated else CAST(FLOOR(CAST(dtmtaskcreated AS DECIMAL(12, 5))) AS DATETIME) end as MessageCreatedDateTime
FROM
dbo.tblAction RIGHT OUTER JOIN dbo.tblTask ON dbo.tblAction.lngactiontaskid = dbo.tblTask.lngtaskid
LEFT OUTER JOIN dbo.tblMessage ON dbo.tblTask.lngtaskmessageid = dbo.tblMessage.lngmessageid
LEFT OUTER JOIN dbo.tblTaskCommentRecipient
RIGHT OUTER JOIN dbo.tblTaskComment ON dbo.tblTaskCommentRecipient.lngTaskCommentID = dbo.tblTaskComment.lngTaskCommentID
ON dbo.tblTask.lngtaskid = dbo.tblTaskComment.lngTaskCommentTaskId
A more seasoned SQL programmer wouldn't join that way. They'd use strictly left joins for clarity (as there is a strictly left joining solution available).
I've unraveled these joins to produce a hierarchy:
Task
Action
Message
TaskComment
TaskCommentRecipient
With associations created in the linq to sql designer, you can reach these levels of the hierarchy:
//note: these aren't outer joins
from t in db.Tasks
let actions = t.Actions
let message = t.Messages
let comments = t.TaskComments
from c in comments
let recipients = c.TaskCommentRecipients
DefaultIfEmpty produces a default element when the collection is empty. Since these are database rows, a default element is a null row. That is the behavior of left join.
query =
(
from t in db.Tasks
from a in t.Actions.DefaultIfEmpty()
from m in t.Messages.DefaultIfEmpty()
from c in t.Comments.DefaultIfEmpty()
from r in c.Recipients.DefaultIfEmpty()
select new Result()
{
TaskStatus = ???
...
}
).Distinct();
Aside: calling Distinct after a bunch of joins is a crutch. #1 See if you can do without it. #2 If not, see if you can eliminate any bad data that causes you to have to call it. #3 If not, call Distinct in a smaller scope than the whole query.
Hope this helps.
SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight], [t0].[ShipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[ShipPostalCode], [t0].[ShipCountry]
FROM [Orders] AS [t0]
LEFT OUTER JOIN ([Order Details] AS [t1]
INNER JOIN [Products] AS [t2] ON [t1].[ProductID] = [t2].[ProductID]) ON [t0].[OrderID] = [t1].[OrderID]
can be write as
from o in Orders
join od in (
from od in OrderDetails join p in Products on od.ProductID equals p.ProductID select od)
on o.OrderID equals od.OrderID into ood from od in ood.DefaultIfEmpty()
select o

Resources