How to execute the following SQL-Query using LINQ or HQL - c#

How can I execute the following query using Castle ActiveRecords and LINQ or HQL?
SELECT a.id, s.classes, COUNT(p.id), MAX(p.date) AS last, MIN(p.date) AS first
FROM account a
LEFT JOIN school s ON s.account_id = a.id
LEFT JOIN user u ON u.account_id = a.id
LEFT JOIN points p ON p.user_id = u.id
WHERE payment = "S"
GROUP BY a.id
The tables are related in the following way:
I also have ActiveRecord classes for all tables with the correct relations defined (if I do the query in steps it works, but it is slow as there are a lot of rows) and I tried the following which didn't worked:
var result = from account in AccountRecord.Queryable
join s in SchoolRecord.Queryable on account equals s.Account into schools
from school in schools.DefaultIfEmpty(null)
join user in UserRecord.Queryable on account equals user.Account
join p in PointsRecord.Queryable on user equals p.User into points
where account.PaymentType == "S"
select new { Account = account, School = school, Count = points.Count() };
which threw the following The method or operation is not implemented-Exception at:
NHibernate.Linq.Visitors.QueryModelVisitor.VisitGroupJoinClause(GroupJoinClause groupJoinClause, QueryModel queryModel, Int32 index)

Found the solution using HQL - I'm still open for a LINQ solution:
HqlBasedQuery query = new HqlBasedQuery(typeof(AccountRecord),
"SELECT a, s, COUNT(p), MIN(p.DateUTC), MAX(p.DateUTC) " +
"FROM AccountRecord a " +
"LEFT JOIN a.Schools s " +
"LEFT JOIN a.Users u " +
"LEFT JOIN u.Points p " +
"WHERE a.PaymentType=:payment GROUP BY a.Id");
query.SetParameter("payment", "S");
var result = from object[] row in (ArrayList)ActiveRecordMediator.ExecuteQuery(query)
select new
{
Account = row[0] as AccountRecord,
School = row[1] as SchoolRecord,
Count = row[2],
First = (new DateTime(1970, 1, 1, 0, 0, 0, 0)).AddSeconds(Convert.ToDouble(row[3])),
Last = (new DateTime(1970, 1, 1, 0, 0, 0, 0)).AddSeconds(Convert.ToDouble(row[4]))
};

I think your Linq query would be something like:
var result = from a in AccountRecord.Queryable
join s in SchoolRecord.Queryable on a.id equals s.account_id
join u in UserRecord.Queryable on a.id equals u.account_id
join p in PointsRecord.Queryable on u.id equals p.user_id
where a.payment == "S"
group by a.id
select new
{
Account = a,
School = s,
Count = p.Count()
};
Though I'm not sure how well NHibernate will handle the group by and Count() combination. You might want to see what the generated SQL ends up being, if it doesn't throw an error.
If that doesn't work, you might want to select the records back, and group/count them in the application instead, more like:
var data = from a in AccountRecord.Queryable
join s in SchoolRecord.Queryable on a.id equals s.account_id
join u in UserRecord.Queryable on a.id equals u.account_id
join p in PointsRecord.Queryable on u.id equals p.user_id
where a.payment == "S"
select new
{
Account = a,
School = s,
Count = c
};
var grouped = data.ToList.GroupBy(x => x.Account.Id);

Related

Selecting max in linq query or rewriting to method chain syntax

I managed to turn this SQL query:
SELECT c.carId, c.Codename, count(c.CarId) as [CarCount],
FROM [DbEfTesting].[dbo].[Cars] c
left join Accessories a on c.CarId = a.CarId
left join CarsPeople cp on cp.CarId = c.CarId
left join People p on cp.PersonId = p.PersonId
group by c.CarId, c.Codename
into a LINQ query:
var x = from c in _context.Cars
join a in _context.Accessories on c.CarId equals a.Car.CarId
join j in _context.CarsPeople on c.CarId equals j.CarId
join p in _context.People on j.PersonId equals p.PersonId
group c by new { c.CarId, c.Codename } into g
select new VMCarAggregate()
{
CarId = g.Key.CarId,
Codename = g.Key.Codename,
CarCount = g.Count()
};
But now I'm lost trying to include a max value e.g the SQL:
SELECT c.carId, c.Codename, count(c.CarId) as [CarCount], max(a.AccessoryId) ...
I googled it and found lots of answers for method syntax. If I were using method chain syntax, I know I could do something like this:
_context.Accessories.Max(a => a.AccessoryId);
but I can't figure out how to do the group by in method chain syntax so either:
How can I convert that query to method syntax?
or
How can I inject a select on the max a.AccessoryId in the LINQ query format?
Try the below code once:
var x = from c in _context.Cars
join a in _context.Accessories equals a.Car.CarId
join j in _context.CarsPeople on c.CarId equals j.CarId
join p in _context.People on j.PersonId equals p.PersonId
group new { c.CarId, c.Codename, a.AccesoryId } by new { c.CarId, c.Codename } into g
select new
{
CarId = g.Key.CarId,
Codename = g.Key.Codename,
CarCount = g.Count(),
MaxAccesory = g.Max(z => z.AccesoryId)
};

Matrix output in ASP.NET MVC LINQ

I have 3 simple tables
tblQual (ID, QualName)
tblPerson (ID, PersonName)
tblPersonQual (ID, PersonID, QualID, ExpiryDate)
I would like to display a matrix with PersonName down the left, QualName at the top and ExpiryDate in the middle. How do I go about doing this?
I've tried this but no joy.
var QualMatrix = from c in db.tblPersonQual
join q in db.tblQual on c.QualID equals q.ID
join p in db.tblPerson on c.PersonID equals p.ID
group c by c.ID into g
select new
{
rowKey = g.Key,
rowData = g.Select(c => new { Qual = q.QualName, Expiry = c.Expiry })
};
In terms of output view something similar to this
As my understanding, you want to display a matrix with QualName, ExpiryDate and QualName. We can use the same query by just modifying group by clause.
var QualMatrix = from c in db.tblPersonQual
join q in db.tblQual on c.QualID equals q.ID
join p in db.tblPerson on c.PersonID equals p.ID
group new { q.QualName,p.PersonName} by new { c.ID,c.ExpiryDate } into g
select new
{
QualName=g.Select(e=>e.QualName).FirstOrDefault(),
ExpirtyDate=g.Key.Expiry,
PersonName=g.Select(e=>e.PersonName).FirstOrDefault(),
};
Hopefully, This will fulfil your requirement.

Using multiple Join Methods instead of join in Linq To SQL

I want to using join method for the following queryin Linq To SQL
What works correctly:
var db = new DatabaseDataContext();
db.DeferredLoadingEnabled = false;
var q = from p in db.Persons
join c in db.Contacts on c.personId equal p.Id
join j in db.Jobs on j.personId equal p.Id
select SetPersonItems(p,c,j) ;
and what is SetPersonItems:
private Person SetPersonItems(Person p, Contact c, Job j)
{
p.Contact = c;
p.Job = j;
return p;
}
What I need:
Now I would like using of Join Method for the above query. Like:
var q = db.Persons.Join<......>(db.Contacts,...).Join<....>(db.Jobs,...).Select....
p.s. Later I will create a dynamic method for the above Multiple Join (Join methods)

LINQ query with sub-query on LEFT JOIN conditions

I have these queries in SQL and LINQ that were built to retrieve the same data. Unfortunately they are retrieving different amount of records (LINQ returns 1555 values, and SQL returns 1969) and I can't figure out why.
Please help me to find out what I'm missing. Follows the queries:
SQL:
SELECT l.Lease_Detail_ID, l.Lease_ID, l.XRef_Lease_ID, v.Vendor_Name, l.Description, c.County, l.Amount, l.Payment_Due_Date,
l.Lease_Type, l.Location_ID, l.Active, l.Expiration_Date, a.Authorized, p.Payment_Date
FROM tblfLeaseDetail AS l
LEFT JOIN tblvVendor AS v ON l.Vendor_ID = v.Vendor_ID
LEFT JOIN tblvCounty AS c ON l.County_ID = c.County_ID
LEFT JOIN tblfAuthorization AS a ON l.Lease_Detail_ID = a.Lease_Detail_ID
AND a.Authorization_ID = (SELECT TOP 1 Authorization_ID
FROM tblfAuthorization
WHERE Lease_Detail_ID = l.Lease_Detail_ID
ORDER BY Authorized_Date)
LEFT JOIN tblfPayment AS p ON l.Lease_Detail_ID = p.Lease_Detail_ID
AND p.Payment_ID = (SELECT TOP 1 Payment_ID
FROM tblfPayment
WHERE Lease_Detail_ID = l.Lease_Detail_ID
ORDER BY payment_date)
ORDER BY l.Lease_Detail_ID
LINQ: (Edited after a few comments)
var leaseList = (from l in leases.tblfLeaseDetails
join v in leases.tblvVendors on l.Vendor_ID equals v.Vendor_ID into lv
from jlv in lv.DefaultIfEmpty()
join c in leases.tblvCounties on l.County_ID equals c.County_ID into lc
from jlc in lc.DefaultIfEmpty()
join a in leases.tblfAuthorizations on l.Lease_Detail_ID equals a.Lease_Detail_ID into la
from jla in la.DefaultIfEmpty()
where jla.Authorization_ID == (from aj in leases.tblfAuthorizations
where aj.Lease_Detail_ID == l.Lease_Detail_ID
orderby aj.Authorized_Date ascending
select aj.Authorization_ID).FirstOrDefault()
join p in leases.tblfPayments on l.Lease_Detail_ID equals p.Lease_Detail_ID into lp
from jlp in lp.DefaultIfEmpty()
where jlp.Payment_ID == (from pj in leases.tblfPayments
where pj.Lease_Detail_ID == l.Lease_Detail_ID
orderby pj.Payment_Date ascending
select pj.Payment_ID).FirstOrDefault()
select new LeaseViewModel()
{
Lease_Detail_ID = l.Lease_Detail_ID,
Lease_ID = l.Lease_ID,
XRef_Lease_ID = l.XRef_Lease_ID,
Vendor_Name = jlv.Vendor_Name,
Description = l.Description,
County = jlc.County,
Amount = l.Amount,
Payment_Due_Date = l.Payment_Due_Date,
Lease_Type = l.Lease_Type.ToString(),
Location_ID = l.Location_ID,
Active = l.Active,
Expiration_Date = l.Expiration_Date,
Authorized = jla.Authorized,
Payment_Date = jlp.Payment_Date
});
EDIT:
After analyzing the run-time SQL query generated by LINQ statement I found out that it's creating the Authorized sub-query in the wrong place. Here is what it looks like:
SELECT [t0].[Lease_Detail_ID], [t0].[Lease_ID], [t0].[XRef_Lease_ID], [t1].[Vendor_Name] AS [Vendor_Name], [t0].[Description], [t2].[County] AS [County], [t0].[Amount], [t0].[Payment_Due_Date], [t0].[Expiration_Date], [t3].[Authorized] AS [Authorized], CONVERT(NVarChar(1),[t0].[Lease_Type]) AS [Lease_Type], [t0].[Location_ID], CONVERT(Int,[t0].[Active]) AS [Active], [t4].[Payment_Date] AS [Payment_Date]
FROM [dbo].[tblfLeaseDetail] AS [t0]
LEFT OUTER JOIN [dbo].[tblvVendor] AS [t1] ON [t0].[Vendor_ID] = ([t1].[Vendor_ID])
LEFT OUTER JOIN [dbo].[tblvCounty] AS [t2] ON [t0].[County_ID] = ([t2].[County_ID])
LEFT OUTER JOIN [dbo].[tblfAuthorization] AS [t3] ON ([t0].[Lease_Detail_ID]) = [t3].[Lease_Detail_ID]
LEFT OUTER JOIN [dbo].[tblfPayment] AS [t4] ON ([t0].[Lease_Detail_ID]) = [t4].[Lease_Detail_ID]
WHERE ([t4].[Payment_ID] = ((SELECT TOP (1) [t5].[Payment_ID] FROM [dbo].[tblfPayment] AS [t5] WHERE [t5].[Lease_Detail_ID] = ([t0].[Lease_Detail_ID])
ORDER BY [t5].[Payment_Date] )))
AND ([t3].[Authorization_ID] = (( SELECT TOP (1) [t6].[Authorization_ID]
FROM [dbo].[tblfAuthorization] AS [t6]
WHERE [t6].[Lease_Detail_ID] = ([t0].[Lease_Detail_ID])
ORDER BY [t6].[Authorized_Date] )))
The problem is that it only made more confuse, once Payment and Authorized joins have exactly the same structure.
after some research I finally found how to do it. Here is the LINQ query that generates the SQL I was trying to get:
var leaseList = (from l in leases.tblfLeaseDetails
join p in leases.tblfPayments
on l.Lease_Detail_ID equals p.Lease_Detail_ID into lp
from jlp in lp.Where(x => x.Payment_ID == (from pj in leases.tblfPayments
where pj.Lease_Detail_ID == l.Lease_Detail_ID
orderby pj.Payment_Date ascending
select pj.Payment_ID).FirstOrDefault()).DefaultIfEmpty()
join a in leases.tblfAuthorizations on l.Lease_Detail_ID equals a.Lease_Detail_ID into la
from jla in la.Where(x => x.Authorization_ID == (from aj in leases.tblfAuthorizations
where aj.Lease_Detail_ID == l.Lease_Detail_ID
orderby aj.Authorized_Date ascending
select aj.Authorization_ID).FirstOrDefault()).DefaultIfEmpty()
join v in leases.tblvVendors on l.Vendor_ID equals v.Vendor_ID into lv
from jlv in lv.DefaultIfEmpty()
join c in leases.tblvCounties on l.County_ID equals c.County_ID into lc
from jlc in lc.DefaultIfEmpty()
select new LeaseViewModel()
{
Lease_Detail_ID = l.Lease_Detail_ID,
Lease_ID = l.Lease_ID,
XRef_Lease_ID = l.XRef_Lease_ID,
Vendor_Name = jlv.Vendor_Name,
Description = l.Description,
County = jlc.County,
Amount = l.Amount,
Payment_Due_Date = l.Payment_Due_Date,
Lease_Type = l.Lease_Type.ToString(),
Location_ID = l.Location_ID,
Active = l.Active,
Expiration_Date = l.Expiration_Date,
Authorized = jla.Authorized,
Payment_Date = jlp.Payment_Date
});

LINQ JOIN and OUTER JOIN - How to turn SQL into LINQ expression

I would like to turn the following SQL query into a LINQ expression (using Entity Framework 6.1). Thus far I have been unable find an acceptable LINQ expression that produces similar results. Any help turning this simple SQL statement into a LINQ express would be appreciated.
SELECT AAG.Id AS GroupId,
A.Id AS ActivityId,
A.Title As Title,
CASE WHEN AA.CompletedOn IS NULL THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS Completed,
COALESCE(AAG.PointValue, 0) + SUM(COALESCE(AQ.PointValue, 0)) AS PointTotal
FROM ActivityAssignmentGroup AAG
INNER JOIN ActivityAssignment AA ON AA.GroupId = AAG.Id
INNER JOIN Activity A ON AA.ActivityId = A.Id
LEFT OUTER JOIN ActivityQuestion AQ ON AQ.ActivityId = A.Id
WHERE AAG.AssignedToId = 6
GROUP BY AAG.Id, A.Id, A.Title, CASE WHEN AA.CompletedOn IS NULL THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END, COALESCE(AAG.PointValue,0)
Without the LEFT OUTER JOIN portion, the below LINQ statement is partially complete, but I cannot figure out the appropriate syntax to add the LEFT OUTER JOIN condition:
var assignments = await (from g in db.AssignmentGroups.AsNoTracking().Where(x => x.AssignedToId == studentTask.Result.PersonId)
join aa in db.ActivityAssignments.AsNoTracking() on g.Id equals aa.GroupId
join a in db.Activities.AsNoTracking() on aa.ActivityId equals a.Id
select new ActivityListViewModel
{
Id = a.Id,
Points = g.PointValue ?? 0,
Title = a.Title,
GroupId = g.Id,
Complete = (aa.CompletedOn != null)
});
Edit:
Thanks for the response Bob. I attempted to use the DefaultIfEmpty and looked at the resultant SQL query generated by the Entity Framework, but it didn't work. Prior to making this post, this is the LINQ statement I attempted:
var assignments = from g in db.AssignmentGroups.AsNoTracking().Where(x => x.AssignedToId == studentTask.Result.PersonId)
join aa in db.ActivityAssignments.AsNoTracking() on g.Id equals aa.GroupId
join a in db.Activities.AsNoTracking() on aa.ActivityId equals a.Id
from aq in db.ActivityQuestions.Where(q => q.ActivityId == a.Id).DefaultIfEmpty()
group aq by new { ActivityId = aq.ActivityId, Title = a.Title, GroupId = g.Id, Points = g.PointValue ?? 0, Completed = (aa.CompletedOn != null) } into s
select new ActivityListViewModel
{
Id = s.Key.ActivityId,
Points = s.Key.Points + s.Sum(y => y.PointValue ?? 0), //g.PointValue ?? 0,
Title = s.Key.Title,
GroupId = s.Key.GroupId,
Complete = s.Key.Completed
};
Of course, it didn't work either. The result was items missing the Id (ActivityId).
You need DefaultIfEmpty() to convert a join to left outer join, documentition from MSDN here
var assignments = await (from g in db.AssignmentGroups.AsNoTracking().Where(x => x.AssignedToId == studentTask.Result.PersonId)
join aa in db.ActivityAssignments.AsNoTracking() on g.Id equals aa.GroupId
join a1 in db.Activities.AsNoTracking() on aa.ActivityId equals a1.Id into a2
from a in a2.DefaultIfEmpty()
select new ActivityListViewModel
{
Id = a == null ? null : a.Id,
Points = g.PointValue ?? 0,
Title = a == null ? null : a.Title,
GroupId = g.Id,
Complete = (aa.CompletedOn != null)
});
Just to close the loop (and thank you Bob Vale)... the query below works:
var assignments = from g in db.AssignmentGroups.AsNoTracking().Where(x => x.AssignedToId == studentTask.Result.PersonId)
join aa in db.ActivityAssignments.AsNoTracking() on g.Id equals aa.GroupId
join a in db.Activities.AsNoTracking() on aa.ActivityId equals a.Id
from aq in db.ActivityQuestions.Where(q => q.ActivityId == a.Id).DefaultIfEmpty()
group aq by new { ActivityId = a.Id, Title = a.Title, GroupId = g.Id, Points = g.PointValue ?? 0, Completed = (aa.CompletedOn != null) } into s
select new ActivityListViewModel
{
Id = s.Key.ActivityId,
Points = s.Key.Points + s.Sum(y => y.PointValue ?? 0), //g.PointValue ?? 0,
Title = s.Key.Title,
GroupId = s.Key.GroupId,
Complete = s.Key.Completed
};
The issue was the group by condition and using ag.ActivityId when I should have used a.Id.

Categories

Resources