SQL Query to LINQ Lambda expression - sql

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);

Related

Convert sql query to EF

Can anybody please help with this SQL query to transform it to EF Linq Expression?
select MI.Id, B.Count, B.Cost, Name, Price, Unity, I.Count, I.Cost, BOL.Date, BOL.type, BOL.Number
from Balance B inner join MaterialsInfo MI on Mi.Id = B.MaterialsId
inner join Income I on MI.Id = I.MaterialsId
inner join BillOfLading BOL on I.BillOfLadingId = BOL.Id
where B.Id in (select Id from (select max(Date), Id from Balance group by Balance.MaterialsId))
and I.Id in (select Id from(select max(Date), Income.Id as Id from Income inner join BillOfLading BOL on Income.BillOfLadingId = BOL.Id group by Income.MaterialsId))
I've been trying to do this for days, so I could really use some help.
Do you need extra info?
Thank you in advance.
Edit
Here is a little part of the diagram, maybe it helps
It seems the subqueries in both of your conditions in where clause is incorrect. Because your
group by column is missing in the select statement
And it is very unclear what you trying to achieve from the subqueries.
Without a clear understanding of your subqueries, all I can prepare for you is this.
var result = await (from balance in yourDBContect.Balances
join material in yourDBContect.MaterialsInfos on balance.MaterialsId equals material.Id
join income in yourDBContect.Incomes on material.Id equals income.MaterialsId
join bill in yourDBContect.BillOfLadings on income.BillOfLadingId equals bill.Id
where Balances.GroupBy(g => g.MaterialsId)
.Select(s => new
{
MaterialsId = s.Key,
MaxDate = s.Max(x => x.Date)
}).ToList().Contains(new { balance.MaterialsId, MaxDate = balance.Date })
&& Incomes.Join(BillOfLadings,
p => p.BillOfLadingId,
e => e.Id,
(p, e) => new { p, e })
.GroupBy(g => g.p.MaterialsId)
.Select(s => new
{
MaterialsId = s.Key,
MaxDate = s.Max(s => s.e.Date)
}).ToList().Contains(new { income.MaterialsId, MaxDate = balance.Date })
select new
{
material.Id,
balance.Count,
balance.Cost,
balance.Name,
balance.Price,
balance.Unity,
ICount = income.Count,
ICost = income.Cost,
bill.Date,
bill.Type,
bill.Number
});
Notes:
Alias for Income.Count and Income.Cost are changed because an object cannot contain exactly same nenter code hereamed property.
Linq for a proper subquery will replace new List<int>().

Convert this SQL with left join to LINQ

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();

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

SQL INNER JOIN with WHERE clause to LINQ format

How would I convert this sql query to LINQ?
SELECT company.ticker, company.primary_analyst,
personnel.last_name, company.research_associate,
company.secondary_associate, company.coverage_status
FROM company
INNER JOIN personnel ON company.primary_analyst = personnel.dpinitials
WHERE personnel.last_name='marley' AND company.associate='ml'
ORDER BY company.coverage_status
It's pretty similar:
var results = from c in company
join p in personnel on c.primary_analyst equals p.dpinitals
where p.last_name == 'marley' and c.associate == 'ml'
orderby c.coverage_status asc
select new
{
c.ticker, c.primary_analyst, p.last_name, c.research_associate,
c.secondary_associate, c.coverage_status
};
Above projects to an anonymous class with the properties you want - if you have an equivalent POCO class in your model you should project to that, if not in many cases you probably should create one.
The solution from #BrokenGlass is perfectly fine.
However, if you have a 1..many relationship it is rarely necessary to use the join operator in LINQ. In this example, if company->personell was 1..many, I would write the query like this:
var results = from c in company
where c.associate == "ml"
from p in c.personnel
where p.last_name == "marley"
orderby c.coverage_status asc
select new
{
c.ticker,
c.primary_analyst,
p.last_name,
c.research_associate,
c.secondary_associate,
c.coverage_status
};
This can also be written using the expression chain syntax:
var results = company.Where(c => c.associate == "ml")
.SelectMany(c => c.personnel, (c, p) => new
{
c.ticker,
c.primary_analyst,
p.last_name,
c.research_associate,
c.secondary_associate,
c.coverage_status
})
.Where(x => x.last_name == "marley")
.OrderBy(x => x.coverage_status)
var list = (from u in db.Users
join c in db.Customers on u.CustomerId equals c.CustomerId
where u.Username == username
select new {u.UserId, u.CustomerId, u.ClientId, u.RoleId, u.Username, u.Email, u.Password, u.Salt, u.Hint1, u.Hint2, u.Hint3, u.Locked, u.Active,c.ProfilePic}).First();

Complex SQL to LINQ conversion with subquery

I am trying to convert this expression into LINQ from SQL, but a bit too difficult for me, maybe you can help me with this!
SELECT TOP (2) RecipeID, UserID, Name, Servings, PreparationTime, TotalTime, DifficultyLevelID, CuisineID, DishID, MainIngredientID, PriceLevelID, FlavourID, Instructions,
Notes, Thumbnail, VideoLink
FROM dbo.Recipes
WHERE (RecipeID NOT IN
(SELECT DISTINCT Recipes_1.RecipeID
FROM dbo.Allergies INNER JOIN
dbo.UsersAllergies ON dbo.Allergies.AllergyID = dbo.UsersAllergies.AllergyID INNER JOIN
dbo.IngredientsAllergies ON dbo.Allergies.AllergyID = dbo.IngredientsAllergies.AllergyID INNER JOIN
dbo.Ingredients ON dbo.IngredientsAllergies.IngredientID = dbo.Ingredients.IngredientID INNER JOIN
dbo.RecipesIngredients ON dbo.Ingredients.IngredientID = dbo.RecipesIngredients.IngredientID INNER JOIN
dbo.Recipes AS Recipes_1 ON dbo.RecipesIngredients.RecipeID = Recipes_1.RecipeID INNER JOIN
dbo.Users ON dbo.UsersAllergies.UserID = dbo.Users.UserID INNER JOIN
dbo.AllergyFactors ON dbo.IngredientsAllergies.AllergyFactorID = dbo.AllergyFactors.AllergyFactorID
WHERE (dbo.Users.UserID = 3) AND (dbo.AllergyFactors.AllergyFactorID < 3)))
It would be easier to help you if you showed us what you have already tried, but a Linq expression like this should give you the same result set
var query = (from rec in context.Recipes
where !(from al in context.Allergies
from ua in context.UsersAllergies.Where(x => al.AllergyID == x.AllergyID)
from ia in context.IngredientsAllergies.Where(x => al.AllergyID == x.AllergyID)
from in in context.Ingredients.Where(x => ia.IngredientID == x.IngredientID)
from ri in context.RecipesIngredients.Where(x => in.IngredientID == x.IngredientID)
from re in context.Recipes.Where(x => ri.RecipeID == x.RecipeID)
from us in context.Users.Where(x => ua.UserID == x.UserID)
from af in context.AllergyFactors.Where(x => ia.AllergyFactorID == x.AllergyFactorID)
where us.UserID == 3 && af.AllergyFactorID < 3
select re.RecipeID)
.Distinct()
.Contains(rec.RecipeID)
select new
{
rec.RecipeID,
rec.UserID,
rec.Name,
rec.Servings,
rec.PreparationTime,
rec.TotalTime,
rec.DifficultyLevelID,
rec.CuisineID,
rec.DishID,
rec.MainIngredientID,
rec.PriceLevelID,
rec.FlavourID,
rec.Instructions,
rec.Notes,
rec.Thumbnail,
rec.VideoLink
}).Take(2);