Convert SQL subselect to LINQ - linq

I have a SQL subselect query that will retrieve the newest history record for a parent record.
I am having trouble converting the following to LINQ:
SELECT *
FROM ProductRequests INNER JOIN ProductRequestHistory
ON ProductRequests.ID = ProductRequestHistory.ProductRequestsID
AND ProductRequestHistory.ID = (SELECT TOP 1 ProductRequestHistory.ID
FROM ProductRequestHistory
WHERE ProductRequestHistory.ProductRequestsID = ProductRequestHistory.ID
ORDER BY ProductRequestHistory.DateCreated DESC)
This is what I have tried, but it doesn't compile:
from productrequests in db.ProductRequests
join productrequesthistories in db.ProductRequestHistories
on new { productrequests.ID, Column1 = (Int32?)Convert.ToInt32(
((from productrequesthistories0 in db.ProductRequestHistories
where
productrequesthistories0.ProductRequestsID == productrequesthistories0.ID
orderby
productrequesthistories0.ID descending
select new {
productrequesthistories0.ID
}).Take(1).First().ID)) }
equals new { ID = (System.Int32?)productrequesthistories.ProductRequestsID, Column1 = productrequesthistories.ID }
select new {
productrequests.ID,
productrequests.ProductRequestNumber,
productrequests.ProjectID,
Column1 = productrequesthistories.ID,
productrequesthistories.Title,
productrequesthistories.Requester,
productrequesthistories.TFSNumber,
productrequesthistories.UseCase,
productrequesthistories.RequestType,
productrequesthistories.FunctionalModule,
productrequesthistories.Description,
productrequesthistories.Reason,
productrequesthistories.Priority,
productrequesthistories.Status,
productrequesthistories.Release,
productrequesthistories.Estimate,
productrequesthistories.Wags,
productrequesthistories.Confidence,
productrequesthistories.Notes,
productrequesthistories.RequirementStatus,
productrequesthistories.RequirementDoco,
productrequesthistories.AlphaRequired,
productrequesthistories.Sprint,
productrequesthistories.Created,
productrequesthistories.DateChanged,
productrequesthistories.Checksum,
productrequesthistories.ProductRequestsID
}

This in VB.NET
Dim result = From request in ProductRequests
Join request2 in ProductRequestHistory
On request.ID = request2.ProductRequestsID
And request2.ID = (From request3 in ProductRequestHistory
Where request3.ProductRequestsID = request3.ID
Order by request3.DateCreated desc
Select request3.ID).First()
Select New With {
.request = request
.history = request2
}
In C#
var result = (from request in db.ProductRequests
Join request2 in ProductRequestHistory
On request.ID == request2.ProductRequestsID
&& request2.ID == (from request3 in ProductRequestHistory
where request3.ProductRequestsID == request3.ID
orderby request3.DateCreated desc
select request3.ID).First()
select new {
request = request
history = request2
}
)
That should do the trick.
Also, in the original SQL statement, I raccomend to change the
AND ProductRequestHistory.ID = (SELECT TOP 1 ProductRequestHistory.ID ......
part to a WHERE clause, since it's not necessary to the JOIN.
So, in the LINQ you can use
Where request2.ID = (From request3 in ProductRequestHistory ......
Moreover, you can optimize the query pre-calculating the TOP 1 ID value:
top1id = (From request3 in ProductRequestHistory
Where request3.ProductRequestsID = request3.ID
Order by request3.DateCreated desc
Select request3.ID).First()
And then use Where request2.ID = top1id.

If I am not mistaken:
var rusult = ProductRequests.Join(ProductRequestHistory,
p => p.ID,
d => d.ProductRequestsID,
(p, d) => p).where(w => w.Id == ProductRequestHistory.OrderByDescending(o=>o.DateCreated).FirstOrDefault(s=>s.ProductRequestsID == s.ID));

Related

LINQ Multiple table left join, distinct count not giving proper result

I am sql query us producing correct result, but when i'm doing the same in LINQ. Output is incorrect. Please let me know where my making mistake.
Following linq query that i created.
LINQ Query:
List<UserModel> Model = (from users in db.UserM
join ct in db.CustT on users.UserId equals ct.UserID into group1
from g1 in group1.DefaultIfEmpty()
join ti in db.TestIn on g1.TestId equals ti.TestID into group2
from g2 in group2.DefaultIfEmpty()
where (users.CustomerId==CustomerId) && (users.RoleId == 4) && (users.Status == 1)
group new
{
g2.TestInvitationID,
g2.TestID,
}
by new
{
users.FirstName,
users.CreatedOn,
users.Email,
users.UserId
} into group4
select new UserModel
{
Name = group4.Key.FirstName,
CreatedOn = group4.Key.CreatedOn,
EmailId = group4.Key.Email,
UserId = group4.Key.UserId,
NoOfTestTaken = group4.Select(x=>x.TestID).Distinct().Count(),
NoOfInvitationsSent = group4.Count(x => x.TestInvitationID != 0)
}).ToList();
SQL Query:
SELECT IsNull(COUNT(distinct TS.TestId),0) AS NoOfTests,
IsNull(COUNT(TS.TestInvitationID),0) AS NoOfInvitations,
UM.Email,
UM.UserId,
UM.FirstName,
UM.CreatedOn
FROM UserM as UM
left JOIN CustT AS CT
ON UM.UserId=CT.UserId
left JOIN TestIn AS TS
ON TS.TestId = CT.TestId
WHERE UM.CustomerId=41
AND UM.RoleId=4
and UM.[Status]=1
GROUP BY UM.UserId, UM.Email, UM.FirstName, UM.CreatedOn
Tables:
"UserM" - columns: UserId, Email, FirstName, CreatedOn
"CustT" - columns: TestId, UserId,
"TestIn" - columns: TestInvitationId, TestId
The difference between SQL COUNT(expr) and LINQ Count is that the former excludes NULL values, which produces a difference when used on right side of a left outer join with no matching records (SQL will produce 0 while LINQ 1). The closest LINQ equivalent is Count(expr != null).
So the direct translation of your SQL query would be like this (note that the generated SQL query could and most likely will be different):
(A side note: When converting SQL query to LINQ, it's good to use the same aliases to make it easier to see the mappings)
var query =
from um in db.UserMasters
join ct in db.CustTests on um.UserId equals ct.UserID
into ctGroup from ct in ctGroup.DefaultIfEmpty() // left outer join
join ts in db.TestInvitaions on ct.TestId equals ts.TestID
into tsGroup from ts in tsGroup.DefaultIfEmpty() // left outer join
where um.CustomerId == UserSession.CustomerId
&& um.RoleId == 4
&& um.Status == 1
group ts by new { um.UserId, um.Email, um.FirstName, um.CreatedOn } into g
select new UserModel
{
Name = g.Key.FirstName,
CreatedOn = g.Key.CreatedOn,
EmailId = g.Key.Email,
UserId = g.Key.UserId,
NoOfTestTaken = g.Where(ts => ts != null).Select(ts => ts.TestID).Distinct().Count(),
NoOfInvitationsSent = g.Count(ts => ts != null)
};
var result = query.ToList();
I suspect that the following row is the problem because is not the same like in your sql:
Linq:
NoOfInvitationsSent = group4.Count(x => x.TestInvitationID != 0)
SQL:
IsNull(COUNT(TS.TestInvitationID),0) AS NoOfInvitations
Due to counting items from a left join Linq should be instead:
NoOfInvitationsSent = group4.Where(i => i != null).Count()
To put it all together, with a bit of better formatting:
var model = (from users in db.UserMasters
join ct in db.CustTests on users.UserId equals ct.UserID into group1
from ct in group1.DefaultIfEmpty()
join ti in db.TestInvitaions on ct.TestId equals ti.TestID into group2
from ct in group2.DefaultIfEmpty()
where users.CustomerId == UserSession.CustomerId &&
users.RoleId == 4 &&
users.Status == 1
group new { ct.TestInvitationID, ct.TestID }
by new
{
users.FirstName,
users.CreatedOn,
users.Email,
users.UserId
} into grouping
select new UserModel
{
Name = grouping.Key.FirstName,
CreatedOn = grouping.Key.CreatedOn,
EmailId = grouping.Key.Email,
UserId = grouping.Key.UserId,
NoOfTestTaken = grouping.Where(i => i != null).Select(x => x.TestID).Distinct().Count(),
NoOfInvitationsSent = grouping.Where(i => i != null).Count()
}).ToList();

Using subquery in entity framework

I am trying to write the entity framework linq query to generate the following SQL. But I am not sure how to use subqueries with entity framework.
The Sql I want to generate is:
Declare #StartDate Datetime2; Set #Startdate = '2014-Feb-16 09:52'
Declare #EndDate Datetime2; Set #Enddate = '2014-Feb-18 09:52'
SELECT
[D].[RefId]
,[D].[StatusId]
,[D].[StatusDate]
,[D].[Reference]
,[RSC].[Event]
,[RSC].[Information]
,[RSC].[CreatedDate]
FROM (
SELECT
[R].[RefId]
,[R].[StatusId]
,[R].[StatusDate]
,[I].[Reference]
,(SELECT TOP 1
[RSC].[ChangeId]
FROM
[dbo].[StateChangeTable] AS [RSC] (nolock)
WHERE
[RSC].[RefId] = [R].[RefId]
ORDER BY
[RSC].[ChangeId] DESC) AS [LastChangeId]
FROM
[dbo].[Table1] AS [R] (nolock)
INNER JOIN
[dbo].[Table2] AS [I] (nolock)
ON
[R].[RefId] = [I].[RefId]
WHERE
[R].[StatusId] IN (4, 6)
AND [R].[StatusDate] between #StartDate and #EndDate
) AS [D]
INNER JOIN
[dbo].[StateChangeTable] AS [RSC] (nolock)
ON
[D].[LastChangeId] = [RSC].[ChangeId
]
And the code I wrote till now is:
return this.DbContext.Table1
.Join(this.DbContext.Table2, rc => rc.RefId, ri => ri.RefId, (rc, ri) => new { rc, ri })
.Join(this.DbContext.StateChangeTable, request => request.ri.RefId, rsc => rsc.RefId, (request, rsc) => new {request, rsc})
.Where(r => (r.rsc.ChangeId == ((from rsc in this.DbContext.StateChangeTable
orderby rsc.ChangeId descending
select rsc.ChangeId).FirstOrDefault())) &&
(r.request.rc.StatusId == 4 || r.request.rc.StatusId == 6) &&
(r.request.rc.StatusDate >= startDateTime && r.request.rc.StatusDate <= endDateTime))
.Select(requestDetails => new StatusDetail
{
RefId = requestDetails.request.rc.RefId,
StatusDate = requestDetails.request.rc.StatusDate,
StatusId = requestDetails.request.rc.StatusId,
Reference = requestDetails.request.ri.DistributionReference.Value,
Event = requestDetails.rsc.Event,
CreatedDate = requestDetails.rsc.CreatedDate,
Information = requestDetails.rsc.Information
}).ToList();
Can some please let me know what I am doing wrong?
Many Thanks
Here is the Full query
var query = (from D in
((from tab1 in DbContext.Table1
join tab2 in DbContext.Table2 on tab1.RefId equals tab2.RefId
where (tab1.StatusId == 4 || tab1.StatusId == 6)
&& (tab1.StatusDate >= startDate && tab1.StatusDate <= endDate)
select new
{
RefId = tab1.RefId,
StatusId = tab1.StatusId,
StatusDate = tab1.StatusDate,
Reference = tab2.Reference,
LastChangeId = (from RSC in DbContext.StateChangeTable
where RSC.RefId == tab1.RefId
orderby RSC.ChangeId descending
select RSC.ChangeId).FirstOrDefault()
}))
join RSC in DbContext.StateChangeTable on D.LastChangeId equals RSC.ChangeId
select new StatusDetail
{
RefId = D.RefId,
StatusId = D.StatusId,
StatusDate = D.StatusDate,
Reference = D.Reference,
Event = RSC.Event,
Information = RSC.Information,
CreatedDate = RSC.CreatedDate
}).ToList();
Don't use .Join() you have to use the navigation properties on your entities.

How to convert exists condition inside where clause in Linq

I want to add following where clause to Linq query. How subquery like below using linq
WHERE (Restaurants.[IsActive] = 1)
AND exists
(
select 1 from APIKeys
where ApiKey = 'on35e5xbt3m4cbcef4e4448t6wssg11o'
and (KeyType = 1
and fk_RestaurantsID = [t2].[RestaurantsID]
or KeyType = 2
and fk_RestaurantGroupID = RG.RestaurantGroupsID
and [t1].[fk_RestaurantsID] in
(SELECT RestaurantsID
FROM Restaurants
WHERE RestaurantGroupsID = RG.RestaurantGroupsID))
)
AND (0 = (COALESCE([t0].[fk_MembersID],0)))
AND (1 = [t0].[fk_BookingStatusID])
AND ([t0].[Email] = 'nike.s#gmail.com')
AND (([t0].[Phone] = '9999999990') OR ([t0].[MobilePhone] = '9999999990'))
Use Any() to produce subquery which translated to EXISTS. E.g. with AdventureWorks database sample:
from p in Products
where p.FinishedGoodsFlag &&
SalesOrderDetails.Any(od => od.ProductID == p.ProductID)
select new { p.ProductID, p.Name }
Will produce following query to database:
SELECT [t0].[ProductID], [t0].[Name]
FROM [Production].[Product] AS [t0]
WHERE ([t0].[FinishedGoodsFlag] = 1) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [Sales].[SalesOrderDetail] AS [t1]
WHERE [t1].[ProductID] = [t0].[ProductID]
))

Linq query joining with a subquery

I am trying to reproduce a SQL query using a LINQ to Entities query. The following SQL works fine, I just don't see how to do it in LINQ. I have tried for a few hours today but I'm just missing something.
SELECT
h.ReqID,
rs.RoutingSection
FROM ReqHeader h
JOIN ReqRoutings rr ON rr.ReqRoutingID = (SELECT TOP 1 r1.ReqRoutingID
FROM ReqRoutings r1
WHERE r1.ReqID = h.ReqID
ORDER BY r1.ReqRoutingID desc)
JOIN ReqRoutingSections rs ON rs.RoutingSectionID = rr.RoutingSectionID
Edit***
I was able to get this working after looking at other examples including the one provided her by Miki. Here is the code that works for me:
First I created a query called route to hold the top record I needed to join to
var route = (from rr in context.ReqRoutings
where rr.ReqID == id
orderby rr.ID descending
select rr).Take(1);
I was then able to join to my requisitions table and the ReqRoutings lookup table
var header = (from h in context.ReqHeaders
join r in route on h.ID equals r.ReqID
join rs in context.ReqRoutingSections on r.RoutingSectionID equals rs.ID
where h.ID == id
select {ReqID = h.ID,
RoutingSection = rs.RoutingSection}
I am using Northwnd sample database
Customers,Orders,Employees table
Here I am getting top 1 order group by customer and order's employeeid
Please let me know If this is matching with your requirement or not
var ord = from o in NDC.Orders
orderby o.OrderID descending
group o by o.CustomerID into g
select new {CustomerID=g.Key,Order=g.OrderByDescending(s=>s.OrderID).First() };
var res1 = from o in ord
join emp in NDC.Employees
on o.Order.EmployeeID equals emp.EmployeeID into oemp
select new {Order=o.Order,employee=oemp };
Response.Write(res1.ToList().Count);
foreach (var order in res1)
{
Response.Write(order.Order.CustomerID + "," +
order.Order.OrderID + ","+
order.Order.EmployeeID+"<br/>");
}
// Above code is working .I have tried to convert your query to linq and replace your datacontext name with 'NDC'
var ord = from rr in NDC.ReqRoutings
orderby rr.ReqRoutingID descending
group rr by rr.ReqID into g
select new
{
ReqID = g.Key,
ReqRoutings = g.OrderByDescending(s => s.ReqRoutingID).First()
};
var res1 = from o in ord
join emp in NDC.ReqRoutingSections on o.ReqRoutings.RoutingSectionID
equals emp.RoutingSectionID into oemp
select new { ReqRoutings = o.ReqRoutings, employee = oemp };
Response.Write(res1.ToList().Count);
foreach (var order in res1)
{
Response.Write(order.ReqRoutings.ReqID + "," +
order.ReqRoutings.ReqRoutingID + "," +
order.ReqRoutings.RoutingSectionID + "<br/>");
}
Please let know if it is help you or not

linq nested query

I have a query that has the following
var myvar = from table in MyDataModel
where.....
select new MyModel
{
modelvar1 = ...,
modelvar2 = (from..... into anothervar)
}
What I want to do is have modelvar2 be a join between the result I currently get from anothervar with another table in MyDataModel.
Thanks
The parenthesis looks more like a subquery than a join. This is how you do a join.
Example tables from the AdventureWorks database.
using (DataClasses1DataContext context = new DataClasses1DataContext())
{
// If you have foreign keys correctly in your database you can
// join implicitly with the "dot" notation.
var myvar = from prod in context.Products
where prod.ListPrice < 10
select new
{
Name = prod.Name,
Category = prod.ProductSubcategory.ProductCategory.Name,
};
// If you don't have foreign keys you need to express the join
// explicitly like this
var myvar2 = from prod in context.Products
join prodSubCategory in context.ProductSubcategories
on prod.ProductSubcategoryID equals prodSubCategory.ProductSubcategoryID
join prodCategory in context.ProductCategories
on prodSubCategory.ProductCategoryID equals prodCategory.ProductCategoryID
where prod.ListPrice < 10
select new
{
Name = prod.Name,
Category = prodCategory.Name,
};
// If you REALLY want to do a subquery, this is how to do that
var myvar3 = from prod in context.Products
where prod.ListPrice < 10
select new
{
Name = prod.Name,
Category = (from prodSubCategory in context.ProductSubcategories
join prodCategory in context.ProductCategories
on prodSubCategory.ProductCategoryID equals prodCategory.ProductCategoryID
select prodCategory.Name).First(),
};
// If you want to get a list from the subquery you can do like this
var myvar4 = from prodCategory in context.ProductCategories
select new
{
Name = prodCategory.Name,
Subcategoreis = (from prodSubCategory in context.ProductSubcategories
where prodSubCategory.ProductCategoryID == prodCategory.ProductCategoryID
select new { prodSubCategory.ProductSubcategoryID, prodSubCategory.Name }).ToList(),
};
}
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]
INNER 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
select o

Resources