Entity Framework join many to many - sql-server

I'm having some trouble 'converting' a T-SQL query to linq using Entity Framework v6.
My working T-SQL query looks like this:
SELECT
[Person].*, [Person_Firm_PersonResponsibility].*
FROM
[Person]
JOIN
[Person_Firm_PersonResponsibility] ON [Person].ID = [Person_Firm_PersonResponsibility].PersonID
WHERE
[Person_Firm_PersonResponsibility].FirmID = 389
AND [Person].ID = 330
In Linq I have the following:
using (var ctx = new MyContext())
{
var result = (from p in ctx.People
join r in ctx.Person_Firm_PersonResponsibility on p.ID equals r.PersonID
where r.FirmID == firmId && p.ID == personId
select p)
.Include("Person_Firm_PersonResponsibility.PersonResponsibility")
.Include("Person_Firm_PersonResponsibility")
.FirstOrDefault();
return result;
}
My goal is to only get a specific Person with responsibilities for a specific firm (firm ID) The relation between Firm, Person and Responsibilities is the many-to-many Person_Firm_PersonResponsibility table which I'm querying against.
The query 'works' in the sense that I get a the person, but it includes all of his/her responsibilities for any firm and not the specific firm (firmId) I've tried removing the .Include(""), but that didn't do it.
Does anyone know how to achieve this?

Try this:
from p in Persons
join r in Person_Firm_PersonResponsibilities
on p.ID equals r.PersonId
where (r.FirmId == 389 && p.ID == 330)
select new {p, r}
Tested using LinqPad which shows the equivalent SQL as being:
-- Region Parameters
DECLARE #p0 Int = 389
DECLARE #p1 Int = 330
-- EndRegion
SELECT [t0].[ID], [t0].[Name], [t1].[FirmId], [t1].[PersonId]
FROM [Person] AS [t0]
INNER JOIN [Person_Firm_PersonResponsibility] AS [t1] ON ([t0].[ID]) = [t1].[PersonId]
WHERE ([t1].[FirmId] = #p0) AND ([t0].[ID] = #p1)

Related

Combine multiple SQL statements into one stored procedure

Okay from these two tables:
SELECT *
FROM Sessions
JOIN Sessions ON Sessions.ID = Sessions.SessionID
I need to count how many seats are taken (counting how many of each sessionID exist)
SELECT DISTINCT
Sessions.SessionID,
COUNT(*) OVER (PARTITION BY SessionID) AS Sess
FROM
Sessions
WHERE
UserID = #UserId
And then if the count Sess is less (an int column in table), select only those sessions from my join (below code is not correct):
SELECT *
FROM Sessions
WHERE UserId = #UserId
How do I combine all of these statements into one stored procedure?
Each select statement works on its own, but I don't know how to combine this into one solution.
WITH EligibleSessions As (
SELECT s.ID
FROM Sessions s
JOIN RegistrantSessions rs ON s.ID = rd.SessionID
WHERE rs.EventID = #EventID
GROUP BY s.ID, s.SeatLimit
HAVING COUNT(rs.*) < s.SeatLimit
)
SELECT s.*
FROM Sessions
INNER JOIN EligibleSessions es on es.ID = s.ID
WHERE s.EventId = #EventId
AND s.SessionTime = #SessionTime
AND s.SessionType = #SessionType
AND s.Active = #SessionActive
There seems like an extra layer of nesting here, but I didn't want to have to include every field in the Sessions table in the GROUP BY.
You may need to repeat some of the WHERE conditions in the CTE. It's also weird to me for the EventID field to repeated in both Sessions and RegistrantSessions. Seems like an indication something is not normalized properly.
If I understand your table structure correctly, we could join a the count query as subselect too:
SELECT *
FROM Sessions
JOIN (
SELECT DISTINCT RegistrantSessions.SessionID
,COUNT(*) OVER (PARTITION BY SessionID) AS SeatCount
FROM RegistrantSessions
JOIN RegistrantSessions ON Sessions.ID = RegistrantSessions.SessionID
WHERE EventID = #EventId
) AS regCounts ON regCounts ON Sessions.ID = regCounts.SessionID
WHERE Sessions.SeatLimit < regCounts.SeatCount
AND EventId = #EventId
AND SessionTime = #SessionTime
AND SessionType = #SessionType
AND Active = #SessionActive

Accessing a new table created after a join

I have joined select columns from 3 tables into a new table using the following:
SELECT A.ExternalID, A.UserDefinedXml.value('(Skin_Sheet/#Label)[1]', 'varchar(3)') AS SS, A.ServiceSiteUid, A.LastModifiedDate, A.PersonUid,
B.FirstName, B.LastName, B.PersonUid,
C.Name
FROM Patient A
INNER JOIN Person B ON B.PersonUid = A.PersonUid
INNER JOIN ListServiceSite C ON C.ServiceSiteUid = A.ServiceSiteUid
WHERE SS IS NOT NULL
ORDER By LastModifiedDate;
This all works but I'm not sure how to reference the column SS created from data extracted from the XML so I can only select the observations in which the value is "Yes" or "No". In R I would have created a new object but I'm not sure how SQL stores this new table if I don't specify what the table name is.
Side note, I did try to insert this into a new table but SQL wasn't letting me because, for some reason, the join resulted in PersonUid being duplicated.
Thank you in advance, I'm very, very new to SQL and trying to learn on the fly.
Conceptually WHERE comes before SELECT, so you need to push the query into a derived table subquery or Common Table Expression (CTE) to reference SS in a WHERE clause. EG
with q as
(
SELECT A.ExternalID, A.UserDefinedXml.value('(Skin_Sheet/#Label)[1]', 'varchar(3)') AS SS, A.ServiceSiteUid, A.LastModifiedDate, A.PersonUid,
B.FirstName, B.LastName, B.PersonUid,
C.Name
FROM Patient A
INNER JOIN Person B ON B.PersonUid = A.PersonUid
INNER JOIN ListServiceSite C ON C.ServiceSiteUid = A.ServiceSiteUid
)
SELECT *
FROM q
WHERE SS IS NOT NULL
ORDER By LastModifiedDate;
This will put your results into a temp table, and avoids the problem of having two columns with the same name:
SELECT
A.ExternalID
,SS = A.UserDefinedXml.value('(Skin_Sheet/#Label)[1]', 'varchar(3)')
,A.ServiceSiteUid
,A.LastModifiedDate
,PersonUid_A = A.PersonUid
,B.FirstName
,B.LastName
,PersonUid_B = B.PersonUid
,C.Name
INTO #TempResults
FROM Patient A
INNER JOIN Person B ON B.PersonUid = A.PersonUid
INNER JOIN ListServiceSite C ON C.ServiceSiteUid = A.ServiceSiteUid
WHERE SS IS NOT NULL
ORDER BY LastModifiedDate;

How can Entity Framework be forced to generate a SQL Inner Join on a nullable Foreign Key?

UPDATE: There is a request about this on the EF Team's UserVoice site. Vote it up
This thread about the Include statement is also related.
Why it's important
Querying with EF6 and Linq rocks! But if it involves a couple of Join Tables, and a nullable Foreign Key, it bogs down with 1000 lines of T-Sql.
If Inner Joins could be forced, it would perform with only 10 lines
For example, an EF6 project references a SQL database. There's a Student table and a Tutor table. Not every Student has a Tutor, so Student.TutorId can be null.
All Student-Tutor info is easily found with T-SQL:
SELECT s.Name, t.Name FROM Student s JOIN Tutor t ON s.TutorId = t.Id
The Linq is like so:
var result = context.Students
.Where(s => s.TutorId != null)
.Select(s => new { StudentName = s.Name, TutorName = s.Tutor.Name })
.ToList();
But EF6 generates this SQL:
SELECT [Extent1].[Name],
CASE WHEN ([Extent2].[FirstName] IS NULL)
THEN N''
ELSE
[Extent2].[Name]
END AS [C1]
FROM [dbo].[Student] AS [Extent1]
LEFT OUTER JOIN [dbo].[Tutor] AS [Extent2] ON [Extent1].[TutorId] = [Extent2].[Id]
WHERE [Extent1].[TutorId] IS NOT NULL
Thanks to Peter for asking about this many years ago. Hopefully there is a better answer now than to abandon Linq.
This GitHub Repository has source code for experimentation.
Entity Framework will generate an inner join if you add a not-null condition after the projection:
var result = context.Students
.Select(s => new { StudentName = s.Name, TutorName = s.Tutor.Name })
.Where(x => x.TutorName != null)
.ToList();
I don't know why it works this way. If EF is smart enough to infer that x.TutorName != null amounts to an inner join, I'd think it should be able to the same with s.TutorId != null.
The only reliable way is if you can construct the LINQ query in such a way that the relationships are "navigated" from the required end to optional end through SelectMany, which I guess makes it not generally applicable.
For demonstration purposes, if you write the sample query like this
var result = db.Tutors
.SelectMany(t => t.Students, (t, s) => new { StudentName = s.Name, TutorName = t.Name })
.ToList();
the generated SQL will be something like this
SELECT
[Extent1].[Id] AS [Id],
[Extent2].[Name] AS [Name],
[Extent1].[Name] AS [Name1]
FROM [dbo].[Tutors] AS [Extent1]
INNER JOIN [dbo].[Students] AS [Extent2] ON [Extent1].[Id] = [Extent2].[TutorId]
At this case you should join tables explicitly, instead of calling tutor as student's property:
var result = (from s in context.Students
join t in context.Tutors
on s.TutorId equals t.Id
select new
{
StudentName = s.Name,
TutorName = t.Name
}).ToLost();

How to convert sql query into Linq to entities?

I am new to joins in linq to entities. I have one query in sql server and want to convert it into LINQ to Entities.
Can someone please provide the solution for the same? Do we have any online tool to convert sql queries to LINQ to entities?
SELECT R.ID,r.Name,u.UserId
FROM Roles R
Left JOIN UserRoles U ON r.Id = u.RoleId
AND [UserId] = '5'
where [UserId] IS NULL
DefaultIfEmpty will result in a left outer join, therefore as you want a simple left join you should do as follows:
var list = (from r in context.Roles
join u in context.UsersRoles on r.Id equals u.RoleId && u.UserId='5' into x
where r.UserId == null
select new
{
r.Id,
r.Name,
u.UserId
}).ToList();
var list = (from r in context.Roles
join ur in context.UsersRoles on r.Id equals ur.RoleId && ur.UserId='5' into x
from y in x.DefaultIfEmpty()
where r.UserId ==null
select new Object
{
Id = r.Id,
Name = r.Name,
ur.UserId
}).ToList();
Note: Not understand your second UserId IS NULL logic

Grouping by ShopName Count Different type of Customer(membership)

We have a 2 tables
Table 1
Shop where we have shop details
ShopNo, ShopName
Table 2
Customer where we have all customer details
CustomerId, CustomerName, MembershipType, ShopId
what we are trying to query is
ShopName SilverMember GoldMember LifeMember
ABC 45 110 1
XYZ 90 0 10
DEF 10 10 7
Is there a way to make it possible
if in Linq well and good else only sql query will do thanks in advance
Regards
Moksha
Something like this should do the trick:
select s.ShopName,
count(c1.CustomerId) as 'SilverMember',
count(c2.CustomerId) as 'GoldMember',
count(c3.CustomerId) as 'LifeMember'
from Shop s
left outer join Customer c1 on c1.ShopId = s.ShopNo and c1.MembershipType = 'SilverMember'
left outer join Customer c2 on c2.ShopId = s.ShopNo and c2.MembershipType = 'GoldMember'
left outer join Customer c3 on c3.ShopId = s.ShopNo and c3.MembershipType = 'LifeMember'
group by s.ShopName
Assuming MembershipType contains the actual VARCHAR values SilverMember, GoldMember, and LifeMember, this should work in T-SQL:
SELECT
[ShopName], COUNT([SilverMember]) AS [SilverMember], COUNT([GoldMember]) AS [GoldMember], COUNT([LifeMember]) AS [LifeMember]
FROM
[Table1]
INNER JOIN [Table2]
ON [Table1].[ShopNo] = [Table2].[ShopId]
PIVOT
(
MAX([MembershipType])
FOR [MembershipType] IN ([SilverMember], [GoldMember], [LifeMember])
) AS [P]
GROUP BY
[ShopName]
Example on SQL Fiddle
If you want to use linq:
var query = from s in context.Shops
join c in context.Customers on s.ShopNo equals c.ShopId
group c by s.ShopName into g
select new
{
ShopName = g.Key,
SilverMember = g.Count(c => c.MembershipType == "SilverMember"),
GoldMember= g.Count(c => c.MembershipType == "GoldMember"),
LifeMember= g.Count(c => c.MembershipType == "LifeMember"),
};

Resources