Linq to SQL combination of aggregate functions - sql

Is there a way to simplify this and make it go faster?
In SQL I use count(*) and count(distinct) in same query which gives me a faster result
Thanks a bunch.
int numberOfUsers = (from u in db.UseLogs
where u.DocID == docID
&& u.LogDate >= fromDate
&& u.LogDate <= to
select u.UserID).Distinct().Count();
int numberOfVisits = (from u in db.UseLogs
where u.DocID == docID
&& u.LogDate >= fromDate
&& u.LogDate <= to
select u).Count();
Conclusion
There is no Count( Distinct) in LINQ. Not EVERY SQL statement can be translated into LINQ.
Thanks for the help guys!

Is this what you're looking for?
from u in db.UseLogs
where u.DocId == docId && u.LogDate >= fromDate && u.LogDate <= to
group u by true into g
select new
{
Count = g.Count(),
DistinctUserCount = g.Select(x => x.UserId).Distinct().Count()
}
You will want to check if it's actually faster than running two queries, or if the performance gain is worth the loss of readability.

Not exactly what you're trying to acheive but may be just as fast:
var query =
from u in db.UseLogs
where u.DocId == docId && u.LogDate >= fromDate && u.LogDate <= to
group u by u.UserID into g
select new
{
Visits = g.Count()
};
int numberOfUsers = query.Count();
int numberOfVisits = query.Sum(q=>q.Visits);
It executes the SQL only once and does the SUMming in memory.

Related

Linq to SQL - Query with multiple joins, sum, grouping, having

I have the following query that I would like to translate to linq.
SELECT
SUM(Credits.CreditAmount)
,Transactions.Id
,Person.FullName
,Person.Id
FROM
Person
JOIN
Transactions
ON Person.AccountId = Transactions.AccountId
JOIN Credits
ON Transactions.Id = Credits.TransactionId
WHERE
Person.Type = 'AccountHolder'
AND Person.Status = 'Active'
AND Transactions.CancelledDate IS NULL
AND Credits.CancelledDate IS NULL
GROUP BY Transactions.AccountId, Person.FullName, Person.Id
HAVING SUM(Credits.CreditAmount) > 20
This is what I came up with. It's an absolute pig... The SQL it generates must be awful.
var query = from p in Person
join t in Transactions
on p.AccountId equalas t.AccountId
join c in Credits
on t.TransactionId = c.TransactionId
where p.Status == "Active" &&
p.Type = "AccountHolder" &&
t.CancelledDate == null &&
c.CancelledDate == null
group new { c.CreditAmount, t.AccountId, p.FullName, p.Id } by new { t.AccountId, p.FullName, p.SSN } into grp
let sumC = grp.Select(x => x.CreditAmount).Sum()
select new
{
TotalCredit = sumC,
AccountId = grp.Key.AccountId,
FullName = grp.Key.FullName,
Id = grp.Key.Id
};
query.Where(p => p.TotalServiceCredit > 20);
The SQL query runs in approximately 3 seconds but I have yet to find the patience to let the Linq query finish. I was wondering if there is something different I should be doing to accomplish this "group, sum, having" query I'm trying to write? Is there something I can do to help Linq generate more performat SQL?
UPDATE
Turns out sgmoore had the right idea. The key to the performance issue was in his answer.
The difference between this
let sumC = grp.Select(x => x.CreditAmount).Sum()
and this
TotalCredit = grp.Sum(x => x.CreditAmount)
was the difference between a query that finishes and one that does not.
See my revised LINQ query below which completes in about the same time as the SQL (5.3 seconds for SQL vs 5.6 seconds for LINQ).
var query = from p in Person
join t in Transactions
on p.AccountId equalas t.AccountId
join c in Credits
on t.TransactionId = c.TransactionId
where p.Status == "Active" &&
p.Type = "AccountHolder" &&
t.CancelledDate == null &&
c.CancelledDate == null
group new { c.CreditAmount, t.AccountId, p.FullName, p.Id } by new { t.AccountId, p.FullName, p.SSN } into grp
select new
{
TotalCredit = grp.Sum(x => x.CreditAmount),
AccountId = grp.Key.AccountId,
FullName = grp.Key.FullName,
Id = grp.Key.Id
};
query.Where(p => p.TotalServiceCredit > 20);
Thanks for all your help!
I don't disagree with WEI_DBA's comment but if you need to do this, then you might find it easier to break this into several queries, eg
var query1 = from p in Person
join t in Transactions on p.AccountId equals t.AccountId
join c in Credits on t.TransactionId equals c.TransactionId
where p.Status == "Active" &&
p.Type = "AccountHolder" &&
t.CancelledDate == null &&
c.CancelledDate == null
select new { c.CreditAmount, t.AccountID, p.FullName, p.Id};
var query2 = (from p in query1
group p by new { p.AccountId, p.FullName, p.Id } into grp
select new
{
TotalCredit = grp.Sum(x => x.CreditAmount),
AccountId = grp.Key.AccountId,
FullName = grp.Key.FullName,
Id = grp.Key.Id
};
var query3 = (from p in query2 where p.TotalCredit > 20 select p);
Then you can let LINQ combine this into one sql command.
As always, it is a good idea to check and verify the actual TSQL generated.

join, group by and count sql to linq conversion

Query:
SELECT COUNT(*) as Avalied
FROM LeaveMaster as lm
JOIN LeaveSubsidary as ls
ON lm.TransMasterId = ls.TransMasterId
WHERE lm.SystemCode = 'abc123' AND lm.EmployeeCode = '0014' AND lm.Status ='Approved'
Group by ls.Leave_Type
How would you convert the above sql query to linq? Thanks :)
Edit :
from p in db.PY_LeaveTransactionMasterTAB.AsEnumerable()
join e in db.PY_LeaveTransactionSubsidaryTAB.AsEnumerable() on p.LV_TransMasterId equals e.LV_TransMasterId into grp
where p.SystemCode == SysCode && p.EmployeeCode == EmpCode && p.Status == "Approved"
group grp by e.Leave_Type // This line is invalid Why?
The above is what I have tried so far, why can't I do e.Leave_Type or what is the right way to do group grp by e.Leave_Type?
You could try this:
(from p in db.PY_LeaveTransactionMasterTAB
join e in db.PY_LeaveTransactionSubsidaryTAB on p.LV_TransMasterId equals e.LV_TransMasterId
where p.SystemCode == SysCode && p.EmployeeCode == EmpCode && p.Status == "Approved"
group new {p,e} by new {e.Leave_Type } into gr
select new
{
Leave_Type = gr.Key.Leave_Type ,
Count = gr.Count()
}).ToList();

Nested selects in LINQ expression, how to?

I don't know how to work with nested selects in LINQ.
How could I convert this SQl expression to LINQ?
Select i.ID, i.Impression,
(Select COUNT(ImpressionsId)
from DiaryImpressions
where DiaryPostsId = '2' AND ImpressionsId = i.ID) as Num from Impressions i
Seriously? DiaryPostsId is a string? Oh well...
from i in context.Impressions
select new {
i.ID,
i.Impressions,
Num = (from d in context.DiaryImpressions
where d.DiaryPostsId == "2"
&& d.ImpressionsId == i.ID
select d).Count()
}
from ...
select new {
i.Id,
i.Impression,
Count = context.DiaryImpressions.Count(d => d.DiaryPostsId == 2 && d.ImpressionsId == i.Id)
}
If you map your objects properly, you can use child relations directly:
Count = i.DiaryImpressions.Count(d => d.DiaryPostsId == 2)

LINQ to NHibernate WHERE EXISTS IN

I've been trying out NHibernate 3 and LINQ to NHibernate. I can't get it to spit out the correct T-SQL query.
Here's my domain model:
Employee { id, name }
Department { id, name }
EmployeeDepartment { id, employee_id, department_id, startdate, enddate }
AttendanceRegistration { id, datetime, employee_id }
Now suppose I'd like to select all AttendanceRegistrations between '2010-10-1' and '2010-11-1' that were connected to a certain department at that time.
DateTime start = new DateTime(2010,10,1);
DateTime end = new DateTime(2010,11,1);
var list =
from ar in session.Query<AttendanceRegistration>()
where
start <= ar.datetime && ar.datetime > end && (
from ed in session.Query<EmployeeDepartment>()
where
ed.startdate <= ar.datetime && ed.enddate > ar.datetime &&
ed.department_id = 1
select ed.employee_id
).Contains(ar.employee_id)
select ar;
The resulting SQL Code will look like this:
select ar.id, ar.datetime, ar.employee_id
from AttendanceRegistration ar
where
'2010-10-1 00:00:00' <= ar.datetime and '2010-11-1' > ar.datetime and exists (
select ed.employee_id
from EmployeeDepartment ed
where
ed.department_id=1 and
ed.startdate <= ar.datetime and
ed.enddate > ar.datetime and
ed.id=ar.employee_id
)
This is ALMOST good :-) The only mistake is
ed.id=ar.employee_id
This should have been:
ed.employee_id=ar.employee_id
Does anybody have ideas how to get LINQ to NHibernate spit out the correct T-SQL query?
I ran into the same problem. I found a way to get around this. Your query can be rewritten as follows. Basically instead of using Contains() operator, add your predicate explicitly in the where clause and use Any() operator.
DateTime start = new DateTime(2010,10,1);
DateTime end = new DateTime(2010,11,1);
var list =
from ar in session.Query<AttendanceRegistration>()
where
start <= ar.datetime && ar.datetime > end && (
from ed in session.Query<EmployeeDepartment>()
where
ed.startdate <= ar.datetime && ed.enddate > ar.datetime &&
ed.department_id == 1
&& ed.employee_id == ar.employee_id
select ed
).Any()
select ar;

Linq 2 SQL Syntax Help

I am almost done converting a MySQL app to Linq2SQL but struggling to get my head round this last SQL query
SELECT a.URL, b.URL AS DuplicateURL
FROM Pages a
INNER JOIN Pages b ON a.MetaDescription = b.MetaDescription
AND a.PageID <> b.PageID
WHERE a.DomainID = #reportid
AND b.DomainID = #reportid
AND LENGTH(TRIM(a.MetaDescription)) > 0
AND a.URL < b.URL
ORDER BY a.URL ASC
Could anymore give me some syntax help on how I would create this query in Linq2SQL?
Any help greatly appreciated
It goes like this:
var DuplicatePages =
from a in DBContext.Pages
join b in DBContext.Pages on a.MetaDescription equals b.MetaDescription
where (a.PageID <> b.PageID) && (a.DomainID == ReportID) &&
(b.DomainID == ReportID) && (a.MetaDescription.Trim().Length > 0) &&
(a.URL < b.URL)
orderby a.URL
select new { Url = a.URL, DuplicateUrl = b.URL };
That's pretty straigtforward:
var reportId = ...;
var duplicates =
from a in db.Pages
from b in db.Pages
where a.MetaDescription == b.MetaDescription
where a.PageID != b.PageID
where a.DomainID == reportId
where b.DomainID == reportId
where a.MetaDescription.Trim().Length > 0
where a.URL < b.URL
orderby a.URL
select new { Url = a.URL, DuplicateUrl = b.Url }