Convert this SQL with left join to LINQ - sql

There are two tables, school and term. The school record must be shown, but the term record may not yet exist, therefore, the term may be null (thus the left join). The left joined table must be filtered by date for the current term if it exists. Can this be done in LINQ?
select school.school_name, term.term_start, term.term_end
from school
left join term on school.school_id = term.school_id and term.term_start <= '2017-10-21' and term.term_end >= '2017-10-21'
where school.active = 1
order by school.school_name
UPDATE:
After some input I have a left join but if a school is missing a term I still cannot make the start and end dates show as null - the school doesn't show at all if I am missing a term, and I want the school to show in the first column. What am I missing?? Here is the latest LinqPad code.
var query = ((from sc in Schools.Where(s => s.Active == 1 )
join t in Terms on sc.School_id equals t.School_id into ts
from tsub in ts.DefaultIfEmpty()
select new {name = sc.School_name,
start = tsub.Term_start,
end = tsub.Term_end})
.Where (o => o.start <= DateTime.Now && o.end >= DateTime.Now))
.OrderBy( o => o.name);
query.Dump();
UPDATE #2
Here is a screen shot of the SQL result, and I am trying to achieve the same thing in LINQ:

var query = from sc in school.Where(s = > s.active == 1 )
join t in term on sc.school_id == t.school_id
select new {name = sc.school_name,
start = t.term_start,
end = term.term_end}
.Where (o => o.start <= '2017-10-21' && o.end >= '2017-10-21')
.OrderBy( o => o.school_name)

I finally figured it out. If you put the .Where() clause on the joined table you will get null values if there is no matching record. Here is the LinqPad LINQ statement that works and it runs perfectly in .NET MVC.
var query = ((from sc in Schools.Where(s => s.Active == 1 )
join t in Terms.Where(x => x.Term_start <= DateTime.Now && x.Term_end >= DateTime.Now) on sc.School_id equals t.School_id into ts
from tsub in ts.DefaultIfEmpty()
select new {name = sc.School_name,
start = tsub.Term_start,
end = tsub.Term_end})
.OrderBy( o => o.name));
query.Dump();

Related

Linq to SQL - Query with multiple joins, sum, grouping, having

I have the following query that I would like to translate to linq.
SELECT
SUM(Credits.CreditAmount)
,Transactions.Id
,Person.FullName
,Person.Id
FROM
Person
JOIN
Transactions
ON Person.AccountId = Transactions.AccountId
JOIN Credits
ON Transactions.Id = Credits.TransactionId
WHERE
Person.Type = 'AccountHolder'
AND Person.Status = 'Active'
AND Transactions.CancelledDate IS NULL
AND Credits.CancelledDate IS NULL
GROUP BY Transactions.AccountId, Person.FullName, Person.Id
HAVING SUM(Credits.CreditAmount) > 20
This is what I came up with. It's an absolute pig... The SQL it generates must be awful.
var query = from p in Person
join t in Transactions
on p.AccountId equalas t.AccountId
join c in Credits
on t.TransactionId = c.TransactionId
where p.Status == "Active" &&
p.Type = "AccountHolder" &&
t.CancelledDate == null &&
c.CancelledDate == null
group new { c.CreditAmount, t.AccountId, p.FullName, p.Id } by new { t.AccountId, p.FullName, p.SSN } into grp
let sumC = grp.Select(x => x.CreditAmount).Sum()
select new
{
TotalCredit = sumC,
AccountId = grp.Key.AccountId,
FullName = grp.Key.FullName,
Id = grp.Key.Id
};
query.Where(p => p.TotalServiceCredit > 20);
The SQL query runs in approximately 3 seconds but I have yet to find the patience to let the Linq query finish. I was wondering if there is something different I should be doing to accomplish this "group, sum, having" query I'm trying to write? Is there something I can do to help Linq generate more performat SQL?
UPDATE
Turns out sgmoore had the right idea. The key to the performance issue was in his answer.
The difference between this
let sumC = grp.Select(x => x.CreditAmount).Sum()
and this
TotalCredit = grp.Sum(x => x.CreditAmount)
was the difference between a query that finishes and one that does not.
See my revised LINQ query below which completes in about the same time as the SQL (5.3 seconds for SQL vs 5.6 seconds for LINQ).
var query = from p in Person
join t in Transactions
on p.AccountId equalas t.AccountId
join c in Credits
on t.TransactionId = c.TransactionId
where p.Status == "Active" &&
p.Type = "AccountHolder" &&
t.CancelledDate == null &&
c.CancelledDate == null
group new { c.CreditAmount, t.AccountId, p.FullName, p.Id } by new { t.AccountId, p.FullName, p.SSN } into grp
select new
{
TotalCredit = grp.Sum(x => x.CreditAmount),
AccountId = grp.Key.AccountId,
FullName = grp.Key.FullName,
Id = grp.Key.Id
};
query.Where(p => p.TotalServiceCredit > 20);
Thanks for all your help!
I don't disagree with WEI_DBA's comment but if you need to do this, then you might find it easier to break this into several queries, eg
var query1 = from p in Person
join t in Transactions on p.AccountId equals t.AccountId
join c in Credits on t.TransactionId equals c.TransactionId
where p.Status == "Active" &&
p.Type = "AccountHolder" &&
t.CancelledDate == null &&
c.CancelledDate == null
select new { c.CreditAmount, t.AccountID, p.FullName, p.Id};
var query2 = (from p in query1
group p by new { p.AccountId, p.FullName, p.Id } into grp
select new
{
TotalCredit = grp.Sum(x => x.CreditAmount),
AccountId = grp.Key.AccountId,
FullName = grp.Key.FullName,
Id = grp.Key.Id
};
var query3 = (from p in query2 where p.TotalCredit > 20 select p);
Then you can let LINQ combine this into one sql command.
As always, it is a good idea to check and verify the actual TSQL generated.

SQL Query to LINQ Lambda expression

I am trying to convert a bit of SQL containing a LEFT OUTER JOIN with a GROUP BY clause into a LINQ Lambda expression.
The SQL I need to convert is:-
SELECT m.MemberExternalPK
FROM Member.Member AS m LEFT OUTER JOIN Member.Account AS a ON m.MemberID = a.MemberID
GROUP BY MemberExternalPK
HAVING COUNT(AccountID) = 0
I have managed to get it working correctly with an INNER JOIN between Member and Accounts like this (for a count of Account = 1) but this does not work for Accounts with a count of 0 (hence the LEFT OUTER JOIN is required):-
Members.Join(Accounts, m => m.MemberID, a => a.MemberID, (m, a) => new {m, a})
.GroupBy(t => t.m.MemberExternalPK, t => t.a)
.Where(grp => grp.Count(p => p.AccountID != null) == 1)
.Select(grp => grp.Key)
I have been trying to experiment with the .DefaultIfEmpty() keyword but have so far been unsuccessful. Any help would be greatly appreciated :)
I think this is what you're after:
Query syntax
var members = new List<Member>
{
new Member {MemberId = 1, MemberExternalPk = 100},
new Member {MemberId = 2, MemberExternalPk = 200}
};
var accounts = new List<Account>
{
new Account {AccountId = 1, MemberId = 1}
};
var query =
from m in members
join a in accounts on m.MemberId equals a.MemberId into ma
from ma2 in ma.DefaultIfEmpty()
where ma2 == null
group ma2 by m.MemberExternalPk into grouped
select new {grouped.Key};
The result of this example is that only the number 200 is returned. As it will only return the MemberExternalPk for members that don't have an account. i.e. As MemberId 2 doesn't have a related Account object, it is not included in the reuslts.
var list = members.GroupJoin(accounts,m=>m.MemberId, a => a.MemberId,
(m,a) => new {m,a})
.Where(x=>x.a.Count()==0)
.Select(s=>s.m.MemberExternalPk);

Left outter join linq

How do i change the training events into a left outer join in training events im very basic at linq so excuse my ignorance its not retrieve records that don't have any trainnevent reference attached to it
var q = from need in pamsEntities.EmployeeLearningNeeds
join Employee e in pamsEntities.Employees on need.EmployeeId equals e.emp_no
join tevent in pamsEntities.TrainingEvents on need.TrainingEventId equals tevent.RecordId
where need.EmployeeId == employeeId
where need.TargetDate >= startdate
where need.TargetDate <= enddate
orderby need.TargetDat
It's best to use where in combination with DefaultIfEmpty.
See here: LEFT JOIN in LINQ to entities?
var query2 = (
from users in Repo.T_Benutzer
from mappings in Repo.T_Benutzer_Benutzergruppen.Where(mapping => mapping.BEBG_BE == users.BE_ID).DefaultIfEmpty()
from groups in Repo.T_Benutzergruppen.Where(gruppe => gruppe.ID == mappings.BEBG_BG).DefaultIfEmpty()
//where users.BE_Name.Contains(keyword)
// //|| mappings.BEBG_BE.Equals(666)
//|| mappings.BEBG_BE == 666
//|| groups.Name.Contains(keyword)
select new
{
UserId = users.BE_ID
,UserName = users.BE_User
,UserGroupId = mappings.BEBG_BG
,GroupName = groups.Name
}
);
var xy = (query2).ToList();
Which is equivalent to this select statement:
SELECT
T_Benutzer.BE_User
,T_Benutzer_Benutzergruppen.BEBG_BE
-- etc.
FROM T_Benutzer
LEFT JOIN T_Benutzer_Benutzergruppen
ON T_Benutzer_Benutzergruppen.BEBG_BE = T_Benutzer.BE_ID
LEFT JOIN T_Benutzergruppen
ON T_Benutzergruppen.ID = T_Benutzer_Benutzergruppen.BEBG_BG

converting SQL join with group by and MAX to Linq

I have Created following query in sql but i am not able to find out how to implement StatusAndTrackingNotes table join in my linq:
SELECT *
FROM [Application] APP
join User USR on APP.ApplicationId = USR.UserApplicationId
join
(Select MAX(TrackingDate) as MaxDateTD,TrackingApplicationId
From StatusAndTrackingNotes
where TrackingLoanType = 1 and ((TrackingStatusCode <= 52 and TrackingStatusCode >= 50) or TrackingStatusCode = 62)
group by TrackingApplicationId) MTND on APP.ApplicationId = MTND.TrackingApplicationId
join Details DTL on APP.ApplicationId = DTL.ApplicationId
join ApplicationFees AF on APP.ApplicationId = AF.ApplicationId
where APP.LatestStatus = 'F' and DTL.Type = 1 and DTL.FundingDate >= '2011-06-01' and DTL.FundingDate <= '2013-06-30'
and AF.FirstRefPaidDate is not null
Kindly help me with the syntax.First i was using using simple query with joins and then
([Linq Query]).GroupBy(i => i.TrackingApplicationId).Select(g => g.OrderByDescending(c => c.TrackingDate).FirstOrDefault());
but it doesnt return any result. Kindly help.
Thanks in advance
I would solve it by creating StatusAndTrackingNotes as separate query and join and group it to the "main query". I.e:
var trackingQuery =
from t in statusAndTrackingNotes
where t.TrackingLoanType == 1 && (t.TrackingStatusCode <= 52 && t.TrackingStatusCode >=50) || t.TrackingStatusCode == 62
group t by t.TrackingApplicationId into trackings
select new
{
TrackingApplicationId = trackings.Key,
MaxDate = trackings.Max(t => t.TrackingDate)
};
var appQuery =
from app in Application
join t in trackingQuery on app.ApplicationId equals t.TrackingApplicationId
/* other joins here */
select new
{
app,
t.MaxDate,
};

Problems with Left join Query LinqToSql

IBookingRepository bookingResp = new BookingRepository();
IQueryable<bookingTest> bookings = bookingResp.GetAllBookingsByView();
var grid = new System.Web.UI.WebControls.GridView();
grid.DataSource = from booking in bookings
join f in getallAttendees on booking.UserID equals f.UserID into fg
from fgi in fg.DefaultIfEmpty() //Where(f => f.EventID == booking.EventID)
where
booking.EventID == id
select new
{
EventID = booking.EventID,
UserID = booking.UserID,
TrackName = booking.Name,
BookingStatus = booking.StatusID,
AttendeeName = booking.FirstName,
// name = account.FirstName,
AmountPaid = booking.Cost,
AttendeeAddress = booking.DeliveryAdd1,
City = booking.DeliveryCity,
Postcode = booking.Postcode,
Date = booking.DateAdded,
hel = fgi == null ? null : fgi.HelmetsPurchased }// Product table
Hi, the above query doesnt executes it gives an error: The specified LINQ expression contains references to queries that are associated with different contexts. Any one can spot the what the problem is with the query.
I think that your getAllAttendees is from a different context than bookings so you won't be able to join them. To give a more exact answer you need to show where bookings and getAllAttendees comes from.