Joining queries with Entity Framework - linq

I have the table
MOVIES_RATING:
MovieID int
MovieRating decimal
I'd like to get 2 values using one query:
select COUNT(*)
FROM [dbo].[MOVIES_RATING]
where [dbo].[MOVIES_RATING].[MovieID] = 78
and
select SUM([dbo].[MOVIES_RATING].Rating)
FROM [dbo].[MOVIES_RATING]
where [dbo].[MOVIES_RATING].[MovieID] = 78
That's what I got in LINQ:
(from p in ef.MOVIES_RATING.Where(r => r.MovieID== movie_id)
let movieRates = ef.MOVIES_RATING.Where(r => r.MovieID == movie_id)
let count = movieRates.Count()
let averageUserRating = movieRates.Sum(c => c.MOVIES_RATING)/count
select new MovieRating {AverageUserRating = averageUserRating, VoteCount = count})
.Take(1);
Looks awful as well as SQL being generated:
SELECT
[Limit1].[MovieID] AS [MovieID],
[Limit1].[C2] AS [C1],
[Limit1].[C1] AS [C2]
FROM ( SELECT TOP 1
[GroupBy1].[A1] AS [C1],
[Extent1].[MovieID] AS [MovieID],
[GroupBy2].[A1] / CAST( [GroupBy1].[A1] AS decimal(19,0)) AS [C2]
FROM [dbo].[MOVIES_RATING] AS [Extent1]
CROSS JOIN (SELECT
COUNT(1) AS [A1]
FROM [dbo].[MOVIES_RATING] AS [Extent2]
WHERE [Extent2].[MovieID] = 78 ) AS [GroupBy1]
CROSS JOIN (SELECT
SUM([Extent3].[Rating]) AS [A1]
FROM [dbo].[MOVIES_RATING] AS [Extent3]
WHERE [Extent3].[MovieID] = 78 ) AS [GroupBy2]
WHERE [Extent1].[MovieID] = 78
) AS [Limit1]
I'm not sure it is best solution, so any help is appreciated.
I know It can be done using stored procedure, but if its could be done using LINQ it would be better.

from r in ef.MOVIES_RATING
group r by r.MovieID into g
where g.Key == movie_id
select new
{
Count = g.Count(),
Sum = g.Sum(r => r.Rating)
}
(or perhaps filter first then group; it probably translates to the same SQL anyway)
Another approach, using Aggregate:
ef.MOVIES_RATING
.Where(r => r.MovieID == movie_id)
.Aggregate(
new { Count = 0, Sum = 0 },
(acc, r) => new { Count = acc.Count + 1, Sum = acc.Sum + r.Rating });
(not sure how it translates to SQL though)

What about simple query:
var query = from m in context.Movies
where m.Id == 78
select new
{
Count = m.MovieRatings.Count(),
Sum = m.MovieRatings.Sum(mr => mr.Rating)
};
var data = query.SingleOrDefault();
Moving average computation to your application code should reduce the SQL query complexity.

Related

How to read data from a nested select query linq

I have a query like the following which is of type linq.
var querymiangin = (from t1 in _context.Apiapplicant
join t2 in _context.ApiApplicantHistory on t1.Id equals t2.ApiApplicantId
join t3 in _context.EntityType on t2.LastReqStatus equals t3.Id
where t1.IsDeleted == false && t1.LastRequestStatus == t2.Id && t3.Name == "granted"
select new { A = t1, B = t2, Year = t1.ApiRequestDate.Substring(0, 4), Month = t1.ApiRequestDate.Substring(5, 2) } into joined
group joined by new { joined.Year, joined.Month, joined.B.LastReqStatus } into grouped
select grouped.Select(g => new { ApiReqDate = g.A.ApiRequestDate, ApiDate = g.B.Date, ApiLastReqStatus = g.B.LastReqStatus, ApiYear = g.Year, ApiMonth = g.Month })).ToList();
In the select part, ApiReqDate and ApiDate has multiple records. Now my problem is for each group of month and year, I have multiple ApiDate and ApiReqDate records and I want for each group based on a condition (t1.LastRequestStatus == t2.Id && t3.Name == "granted") by using GetPersianDaysDiffDate() method, obtain the difference between ApiReqDate and its related ApiDate records for each month and then find their average in that month.
For doing that, I have written code like this:
var avgDateDiff = querymiangin.DefaultIfEmpty()
.GroupBy(x => new { x.ApiYear, x.ApiMonth }, (key, g) => new
{
key.ApiYear,
key.ApiYear,
Avg = g.Average(y => GetPersianDaysDiffDate(y.ApiReqDate,y.ApiDate))
})
.ToList();
But the problem is each parameter x.ApiYear, x.ApiMonth,y.ApiReqDate,y.ApiDate are unknown and it shows me error. I appreciate if anyone can suggest me a solution for that.
1 - For the first request querymiangin, you don't need to group by statement, change little the code to :
var querymiangin = (from t1 in Apiapplicant
join t2 in ApiApplicantHistory on t1.Id equals t2.ApiApplicantId
join t3 in EntityType on t2.LastReqStatus equals t3.Id
where t1.IsDeleted == false && t1.LastRequestStatus == t2.Id && t3.Name == "granted"
select new
{
ApiReqDate = t1.ApiRequestDate,
ApiDate = t2.Date,
ApiYear = t1.ApiRequestDate.Substring(0, 4),
ApiMonth = t1.ApiRequestDate.Substring(5, 2)
}).ToList();
2 - For the second query avgDateDiff, use GroupBy by ApiYear and ApiMonth and calculate the Average, like :
var avgDateDiff = querymiangin
.GroupBy(x => new { x.ApiYear, x.ApiMonth }, (key, g) => new
{
key.ApiYear,
key.ApiMonth,
Avg = g.Average(y => GetPersianDaysDiffDate(y.ApiReqDate, y.ApiDate))
}).ToList();
I hope you find this helpful.

Linq Query with Max Effective Date

Having trouble understanding how i can convert the following SQL Query into LINQ. Specifically the MAX effective date parts.
SELECT A.NAME, X.XLATLONGNAME AS ACTION, C.DESCR, B.EFFDT
FROM ACTN_REASON_TBL C, XLATTABLE X, PERSONAL_DATA A, JOB B
WHERE A.EMPLID = B.EMPLID
AND B.ACTION = C.ACTION(+)
AND B.ACTION_REASON = C.ACTION_REASON(+)
AND (C.EFFDT = (SELECT MAX(C_ED.EFFDT) FROM ACTN_REASON_TBL C_ED
WHERE C.ACTION = C_ED.ACTION
AND C.ACTION_REASON = C_ED.ACTION_REASON
AND C_ED.EFFDT <= B.EFFDT)
OR C.EFFDT IS NULL)
AND X.FIELDNAME = 'ACTION'
AND B.ACTION = X.FIELDVALUE
AND X.EFFDT = (SELECT MAX(X_ED.EFFDT) FROM XLATTABLE X_ED
WHERE X.FIELDNAME = X_ED.FIELDNAME
AND X.LANGUAGE_CD = X_ED.LANGUAGE_CD
AND X.FIELDVALUE = X_ED.FIELDVALUE
AND X_ED.EFFDT <= SYSDATE)
AND B.ACTION_DT BETWEEN sysdate - 30 AND sysdate
AND B.ACTION NOT IN ('EOI','NBY','LIF','FSC','LOA','LTD','PLA','RFL','PAY')
ORDER BY A.NAME, B.EFFDT DESC
This is what i have so far for my LINQ query...
public ActionResult RecentTransaction()
{
RecentTransViewModel recTransModel = new RecentTransViewModel();
var minusThirty = DateTime.Today.AddDays(-90);
var today = DateTime.Now;
var exceptionList = new List<string> { "EOI", "NBY", "LIF", "FSC", "LOA", "LTD", "PLA", "RFL", "PAY" };
var transaction = (from p in recentTrans.PERSONAL_DATA
join j in recentTrans.JOB on p.EMPLID equals j.EMPLID
join a in recentTrans.ACTN_REASON_TBL on j.ACTION_REASON equals a.ACTION_REASON
join x in recentTrans.XLATTABLE_VW on j.ACTION equals x.FIELDVALUE
where x.FIELDNAME == "ACTION"
where j.ACTION_DT >= minusThirty
where j.ACTION_DT <= today
where !exceptionList.Contains(j.ACTION)
select new RecentTransViewModel
{
Name = p.NAME,
Action = x.XLATLONGNAME,
Descr = a.DESCR,
EffectiveDate = j.EFFDT
}).OrderBy(d => d.Name)
.ToList();
return View(transaction);
Any help is greatly appriciated!
LINQ allows you to add nested Queries within the same contex so that you can:
select new RecentTransViewModel
{
Name = p.NAME,
Action = x.XLATLONGNAME,
Descr = a.DESCR,
EffectiveDate = EffectiveDate = (from eft in recentTrans.XLATTABLE_VW
where eft.FIELDNAME == x.FIELDNAME AND eft.LANGUAGE_CD = X.LANGUAGE_CD AND
eft.FIELDVALUE = x.FIELDVALUE AND eft.EFFDT <= SYSDATE
select eft).Max(c=> c.EFFDT)
}).OrderBy(d => d.Name)
.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, convert sql query to linq for VB.net

I am trying to get fields from one table and max(expirationDate) from another table. This is my original linq query:
tanks = (From t In db.Table1 _
Where t.CompanyID = txtCompanyID.Text.Trim() _
Join d In db.Table2 On t.TankID Equals d.TankID
Order By d.ExpireDate Descending
Select t).ToList
that brings back multiple expireDates, I only want the max(expireDate) for each record
The following sql query works, I just need to put it into a linq query
select t1.*,
(Select MAX(Table2.ExpireDate)
from Table2 as t2
where t2.TankID = t1.TankID)
as max_expire_date
from Table1 as t1
where t1.CompanyID = '5467'
order by t1.CargoTankID
Does anybody know how to get this into linq? Thanks
tanks = db.Table1.Where( t => t.CompanyId == txtCompanyID.Text.Trim() )
.Join( db.Table2, t1 => t1.TankID, t2 => t2.TankID, (t1,t2) => new { Company = t1, t2.ExpireDate } )
.GroupBy( t => t.Company, (c,e) => new { Company = c Expiration = e.Max( x => x.ExpireDate ) } )
.ToList();
Best guess at VB
Dim tanks = db.Table1.Where( Function(t) t.CompanyId == txtCompanyID.Text.Trim() ) _
.Join( db.Table2, Function(t1) t1.TankID, Function(t2) t2.TankID, Function(t1,t2) New Thing With { .Company = t1, .ExpireDate = t2.ExpireDate } ) _
.GroupBy( Function(t) t.Company, Function(c,e) New Thing With { .Company = c, .Expiration = e.Max( Function(x) x.ExpireDate ) } ) _
.ToList()

Resources