Linq query taking too long to process results - c#

I have 3 tables
ERPEntry
ERPEntryType
ERPApp
I am trying to get data from these 3 tables using the below queries:.. The table ERPApp is in different context than the other 2 tables, that is the reason i am using 2 queries
var res = (from s in ERPDB.ERPEntrys
join t in ERPDB.ERPEntryTypes
on s.EntryTypeID equals t.EntryTypeID
where s.UserIDAdded == '250176'
select new {s.EntryTypeID, s.DateAdded, t.EntryTypeName, s.AppID }).OrderByDescending(d => d.DateAdded).Take(10).ToArray();
var erpResult = (from a in APPDB.ERPApps.AsEnumerable()
join b in res on a.AppId equals b.AppId
select new ERPInfo
{
EntryId = b.EntryID,
EntryType = b.EntryTypeName,
ERPApp = a.ApplicationName,
DateAdded = b.DateAdded
}).ToList();
I got the desired result but the 2nd query is taking almost 4 minutes to process and return result into varibale erpResult... any help on how can i resolve this performace issue ?

First, the answer:
var res = (from s in ERPDB.ERPEntries
join t in ERPDB.ERPEntryTypes
on s.EntryTypeID equals t.EntryTypeID
where s.UserIdAdded == "250176"
select new { s.EntryID, s.EntryTypeID, s.DateAdded, t.EntryTypeName, s.AppId })
.OrderByDescending(d => d.DateAdded).Take(10).ToArray();
/* Which immediately executes:
exec sp_executesql N'SELECT TOP (10) [t0].[EntryID], [t0].[EntryTypeID],
[t0].[DateAdded], [t1].[EntryTypeName], [t0].[AppId]
FROM [dbo].[ERPEntry] AS [t0]
INNER JOIN [dbo].[ERPEntryType] AS [t1] ON [t0].[EntryTypeID] = [t1].
[EntryTypeID]
WHERE [t0].[UserIdAdded] = #p0
ORDER BY [t0].[DateAdded] DESC',N'#p0 varchar(8000)',#p0='250176'
*/
// Get the distinct AppID values in res
// Executes in memory
var distinctApps = (from r in res select r.AppId).Distinct();
// Query APPDB with the distinct values
var matchingApps = (from a in APPDB.ERPApps where distinctApps
.Contains(a.AppId) select new { a.AppId, a.ApplicationName }).ToArray();
/* Which immediately executes this efficient query:
exec sp_executesql N'SELECT [t0].[AppId], [t0].[ApplicationName]
FROM [dbo].[ERPApp] AS [t0]
WHERE [t0].[AppId] IN (#p0, #p1, #p2, #p3)',N'#p0 bigint,#p1 bigint,#p2
bigint,#p3 bigint',#p0=101,#p1=123,#p2=125,#p3=129
*/
var erpResultWithAppNames = (from a in matchingApps
join b in res on a.AppId equals b.AppId
select new
{
EntryId = b.EntryID,
EntryType = b.EntryTypeName,
ERPApp = a.ApplicationName,
DateAdded = b.DateAdded
}).ToList();
Extra notes:
As already mentioned in the comments, the call to .AsEnumerable() on the APPDB.ERPApps table causes the whole table to be loaded into memory as objects.
Facts:
You had to load 25.000 rows into memory (and convert them to objects),
You are not only loading 25.000 rows. You are loading 25.000 x 173 cells and create 25.000 objects with 173 fields each.
if you could just load the two fields you need (AppId and ApplicationName) instead of all the 173 fields (with whatever data they have) the performance would improve, but would still be inefficient considering what you are trying to achieve.
The current performance problem is in part due to transferring the whole table with all its 173 fields to your server, and in part to mapping all these rows to objects and fields to properties. The impact of each can be measured as further research.
Linq To SQL. When checked with SqlProfiler:
from a in APPDB.ERPApps.AsEnumerable() join b ...
// PROFILED QUERY:
SELECT [t0].[AppId], [t0].[ApplicationName], [t0].[Field1], [t0].[Field2],
[t0].[Field3], [t0].[Field4], [t0].[Field5], [t0].[Field6], [t0].[Field7],
[t0].[Field8], [t0].[Field9], ... ... ... ... [t0].[Field176], [t0].[Field173],
FROM [dbo].[ERPApp] AS [t0]
But:
var erpResult = (from a in APPDB.ERPApps
select new { AppId = a.AppId, ApplicationName = a.ApplicationName }
).ToArray();
// PROFILED QUERY:
SELECT [t0].[AppId], [t0].[ApplicationName]
FROM [dbo].[ERPApp] AS [t0]

Related

Improving LINQ query for many-to-many relation

I have a database with the following schema:
Now, I'm trying to pull all landingpages for a domain and sort those by the first UrlFilter's FilterType that matches a certain group. This is the LINQ I've come up with so far:
var baseQuery = DbSet.AsNoTracking()
.Where(e => EF.Functions.Contains(EF.Property<string>(e, "Url"), $"\"{searchTerm}*\""))
.Where(e => e.DomainLandingPages.Select(lp => lp.DomainId).Contains(domainId));
var count = baseQuery.Count();
var page = baseQuery
.Select(e => new
{
LandingPage = e,
UrlFilter = e.LandingPageUrlFilters.FirstOrDefault(f => f.UrlFilter.GroupId == groupId)
})
.Select(e => new
{
e.LandingPage,
FilterType = e.UrlFilter == null ? UrlFilterType.NotCovered : e.UrlFilter.UrlFilter.UrlFilterType
})
.OrderBy(e => e.FilterType)
.Skip(10).Take(75).ToList();
Now, while this technically works, it's quite slow with execution times ranging from 10-30 seconds, which is not good enough for the use case. The LINQ is translated to the following SQL:
SELECT [l1].[Id], [l1].[LastUpdated], [l1].[Url], CASE
WHEN (
SELECT TOP(1) [l].[LandingPageId]
FROM [LandingPageUrlFilters] AS [l]
INNER JOIN [UrlFilters] AS [u] ON [l].[UrlFilterId] = [u].[Id]
WHERE ([l1].[Id] = [l].[LandingPageId]) AND ([u].[GroupId] = #__groupId_3)) IS NULL THEN 4
ELSE (
SELECT TOP(1) [u0].[UrlFilterType]
FROM [LandingPageUrlFilters] AS [l0]
INNER JOIN [UrlFilters] AS [u0] ON [l0].[UrlFilterId] = [u0].[Id]
WHERE ([l1].[Id] = [l0].[LandingPageId]) AND ([u0].[GroupId] = #__groupId_3))
END AS [FilterType]
FROM [LandingPages] AS [l1]
WHERE CONTAINS([l1].[Url], #__Format_1) AND #__domainId_2 IN (
SELECT [d].[DomainId]
FROM [DomainLandingPages] AS [d]
WHERE [l1].[Id] = [d].[LandingPageId]
)
ORDER BY CASE
WHEN (
SELECT TOP(1) [l2].[LandingPageId]
FROM [LandingPageUrlFilters] AS [l2]
INNER JOIN [UrlFilters] AS [u1] ON [l2].[UrlFilterId] = [u1].[Id]
WHERE ([l1].[Id] = [l2].[LandingPageId]) AND ([u1].[GroupId] = #__groupId_3)) IS NULL THEN 4
ELSE (
SELECT TOP(1) [u2].[UrlFilterType]
FROM [LandingPageUrlFilters] AS [l3]
INNER JOIN [UrlFilters] AS [u2] ON [l3].[UrlFilterId] = [u2].[Id]
WHERE ([l1].[Id] = [l3].[LandingPageId]) AND ([u2].[GroupId] = #__groupId_3))
END
OFFSET #__p_4 ROWS FETCH NEXT #__p_5 ROWS ONLY
Now my question is, how can I improve the execution time of this? Either by SQL or LINQ
EDIT: So I've been tinkering with some raw SQL and this is what I've come up with:
with matched_urls as (
select l.id, min(f.urlfiltertype) as Filter
from landingpages l
join landingpageurlfilters lpf on lpf.landingpageid = l.id
join urlfilters f on lpf.urlfilterid = f.id
where f.groupid = #groupId
and contains(Url, '"barz*"')
group by l.id
) select l.id, 5 as Filter
from landingpages l
where #domainId in (
select domainid
from domainlandingpages dlp
where l.id = dlp.landingpageid
) and l.id not in (select id from matched_urls ) and contains(Url, '"barz*"')
union select * from matched_urls
order by Filter
offset 10 rows fetch next 30 rows only
This performs somewhat okay, cutting the execution time down to ~5 seconds. As this is to be used for a table search I would however like to get it down even further. Is there any way to improve this SQL?
You're right to have a look at the generated SQL. In general, I would advise to learn SQL, write a performing SQL query and work your way back (either use a stored procedure or raw SQL, or design your LINQ query with that same philosophy.
I suspect this will be better (not tested):
var page = (
from e in baseQuery
let urlFilter = e.LandingPageUrlFilters.OrderBy(f => f.UrlFilterType).FirstOrDefault(f => f.UrlFilter.GroupId == groupId)
let filterType = urlFilter == null ? UrlFilterType.NotCovered : e.UrlFilter.UrlFilter.UrlFilterType
select new
{
LandingPage = e,
FilterType = filterType
}
).Skip(10).Take(75).ToList();
one of the way to improve the execution time is see execution plan in SSMS (SQL Server Management Studio).
After look on the execution plan you can design some indexes, or if you have no experiences with this, you can see if SSMS recommends some indexes.
Next try to create the indexes and execute the query again and see if execution time was improved.
Note: this is only one of many possible ways to improve execution time...

Performing two queries in a single round trip to the database

I have the following code to perform a full-text search. It creates a query, gets the total number of rows returned by that query and then retrieves the actual rows for only the current page.
// Create IQueryable
var query = from a in ArticleServerContext.Set<Article>()
where a.Approved
orderby a.UtcDate descending
select a;
// Get total rows (needed for pagination logic)
int totalRows = query.Count()
// Get rows for current page
query = query.Skip((CurrentPage - 1) * RowsPerPage).Take(RowsPerPage);
This works fine, but it requires two round trips to the database. In the interest of optimizing the code, is there any way to rework this query so it only had one round trip to the database?
Yes, you can perform this two operations with the help of the only one query to database:
// Create IQueryable
var query = from a in ArticleServerContext.Set<Article>()
where a.Approved
orderby a.UtcDate descending
select new { a, Total = ArticleServerContext.Set<Article>().Where(x => x.Approved).Count() };
//Get raw rows for current page with Total(Count) field
var result = query.Skip((CurrentPage - 1) * RowsPerPage).Take(RowsPerPage).ToList();
//this data you actually will use with your logic
var actualData = result.Select(x => x.a).ToList();
// Get total rows (needed for pagination logic)
int totalRows = result.First().Total;
If you use MSSQL query wil be look that way:
SELECT
[Extent1].[ID] AS [ID],
[Extent1].[UtcDate] AS [UtcDate],
[Extent1].[Approved] AS [Approved],
[GroupBy1].[A1] AS [C1]
FROM [dbo].[Articles] AS [Extent1]
CROSS JOIN (SELECT
COUNT(1) AS [A1]
FROM [dbo].[Articles] AS [Extent2]
WHERE [Extent2].[Approved] ) AS [GroupBy1]
WHERE [Extent1].[Approved]
ORDER BY [Extent1].[UtcDate] DESC
I'm not sure whether it's worth enough, but it's doable under the following constraints:
(1) CurrentPage and RowsPerPage are not affected by the totalRows value.
(2) The query is materialized after applying the paging parameters.
The trick is to use group by constant value, which is supported by EF. The code looks like this:
var query =
from a in ArticleServerContext.Set<Article>()
where a.Approved
// NOTE: order by goes below
group a by 1 into allRows
select new
{
TotalRows = allRows.Count(),
PageRows = allRows
.OrderByDescending(a => a.UtcDate)
.Skip((CurrentPage - 1) * RowsPerPage).Take(RowsPerPage)
};
var result = query.FirstOrDefault();
var totalRows = result != null ? result.TotalRows : 0;
var pageRows = result != null ? result.PageRows : Enumerable.Empty<Article>();

Long running query in EF 5.0

The following query should return about 7200 records:
using (var context = new RapEntities())
{
context.Configuration.ProxyCreationEnabled = false;
var query = from i in context.QbTxnItems.AsNoTracking()
where (i.ListType == "Invoice")
&& !context.Payments.Any(p => p.QbTxnId == i.QbTxnId && p.QbTxnId != null)
&& !context.QbTxnIgnores.Any(ti => ti.QbTxnId == i.QbTxnId)
orderby i.RefNumber
select i;
var items = RapEntities.GetList(query);
The sql generated (from sql server profiler:
SELECT
[Extent1].[QbTxnItemId] AS [QbTxnItemId],
[Extent1].[ListType] AS [ListType],
[Extent1].[QbTxnId] AS [QbTxnId],
[Extent1].[QbEditSequence] AS [QbEditSequence],
[Extent1].[TxnNumber] AS [TxnNumber],
[Extent1].[RefNumber] AS [RefNumber],
[Extent1].[TxnDate] AS [TxnDate],
[Extent1].[TxnAmt] AS [TxnAmt],
[Extent1].[IsPaid] AS [IsPaid],
[Extent1].[IsCleared] AS [IsCleared],
[Extent1].[LastGetAll] AS [LastGetAll],
[Extent1].[GetIsCleared] AS [GetIsCleared],
[Extent1].[LastModified] AS [LastModified],
[Extent1].[Version] AS [Version],
[Extent1].[RecordStatus] AS [RecordStatus],
[Extent1].[UserId] AS [UserId],
[Extent1].[TableID] AS [TableID]
FROM [dbo].[QbTxnItems] AS [Extent1]
WHERE (N'Invoice' = [Extent1].[ListType]) AND ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Payments] AS [Extent2]
WHERE ([Extent2].[QbTxnId] = [Extent1].[QbTxnId]) AND ([Extent2].[QbTxnId] IS NOT NULL)
)) AND ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[QbTxnIgnores] AS [Extent3]
WHERE [Extent3].[QbTxnId] = [Extent1].[QbTxnId]
))
ORDER BY [Extent1].[RefNumber] ASC
will not complete in any reasonable amount of time when executed from Entity Framework, but executes instantaneously from SSMS.
Using take(200) to limit the number of rows to 200, the query runs in about 50 msecs even when called from EF. Increasing the number of rows to 500 increases the time to over 5 seconds.
This seems to be inappropriate performance. EF must be capable of returning more than a few hundred rows in a reasonable amount of time. Are there any settings that can be adjusted to increase the capability for running larger queries from EF?
Looks to me like you could speed this up by actually doing some Joins here.
Try this:
var query = (from i in context.QbTxnItems.AsNoTracking()
join p in context.Payments on i.QbTxnId equals p.QbTxnId
join qi in context.QbTxnIgnores on i.QbTxnId equals qi.QbTxnId
where (i.ListType == "Invoice")
select i).OrderBy(i => i.RefNumber);

nhibernate - getting having count working with a subquery

So I have a many to many relationship between something known as Specialism and SpecialismCombo. What I'm trying to do is take an int[] of ids and check if there is a combo already that contains the specialisms with those ids.
I was close but not quite right.
Say I have specialisms with Ids 1 and 3 and I create a combo with those specialisms.
If I pass in 3 & 1 then it returns the expected combo id.
If I pass in 1 then it returns the combo id that has both 1 and 3.
I can't just rely on total number of specialisms associated with the combo. Because if a combo has two items, 1 and 4 and the items being matched on are 1 and 3 I don't want this coming back as a matched combo.
So it's like I do need the count of this result, and match the count of total specialisms associated to the combo. I don't quite get whether I'm after a subquery or detatchedcriteria or how to get the result I want using nhibernate criteria. Thanks for your help!
int[] SpecialismIds = ArrayExtensions.ConvertArray<int>(idCollection.Split(new char[] { '|' }));
ICriteria query = m_SpecialismComboRepository.QueryAlias("sc");
query.CreateAlias("sc.Specialisms", "s", NHibernate.SqlCommand.JoinType.InnerJoin);
ICriterion lastCriteria = null;
foreach(int i in SpecialismIds)
{
ICriterion currentCriteria = Restrictions.Eq("s.SpecialismId", i);
if (lastCriteria != null)
lastCriteria = Restrictions.Or(lastCriteria, currentCriteria);
else
lastCriteria = currentCriteria;
}
if (lastCriteria != null)
query.Add(lastCriteria);
IProjection IdCount = Projections.Count("s.SpecialismId").As("IdCount");
query.SetProjection(
Projections.GroupProperty("sc.SpecialismComboId"),
IdCount
);
query.Add(Restrictions.Eq(IdCount, SpecialismIds.Count()));
var comboId = query.List();
The sql being generated is:
SELECT this_.SpecialismComboId as y0_, count(s1_.SpecialismId) as y1_
FROM dbo.SpecialismCombo this_
inner join SpecialismComboSpecialism specialism3_ on this_.SpecialismComboId=specialism3_.SpecialismId
inner join dbo.Specialism s1_ on specialism3_.SpecialismComboId=s1_.SpecialismId WHERE s1_.SpecialismId = #p0
GROUP BY this_.SpecialismComboId HAVING count(s1_.SpecialismId) = #p1',N'#p0 int,#p1 int',#p0=3,#p1=1
EDIT - It seems like I either need the having to be something like...
HAVING count(s1_.SpecialismId) = (select count(SpecialismId)
from specialismComboSpecialism
where SpecialismComboId = y0
group by SpecialismComboId) == #p2
Or maybe it's simpler than that and I need to exclude SpecalismCombos where the combo.specialisms are not in the collection of ids.
Ie. if the combo has specialisms 1 and 3 but the collection only has 1.. then we could exclude this combo based on 3 not being in the collection…
Edit 8/8/2011
Went back to focusing on how to get the result I needed in SQL - and I believe this query works.
WITH CustomQuery AS
(
SELECT sc.SpecialismComboId,
count(s.SpecialismId) AS ItemCount
FROM SpecialismCombo sc
inner join SpecialismComboSpecialism scs on sc.SpecialismComboId = scs.SpecialismComboId
inner join Specialism s on s.SpecialismId = scs.SpecialismId
GROUP BY sc.SpecialismComboId
HAVING count(s.SpecialismId) = 2
)
SELECT CustomQuery.SpecialismComboId FROM CustomQuery
INNER JOIN SpecialismComboSpecialism scs on CustomQuery.SpecialismComboId = scs.SpecialismComboId
WHERE scs.SpecialismId in (1,4)
GROUP BY CustomQuery.SpecialismComboId
HAVING count(scs.SpecialismId) = 2
So now I just need to figure out how to call this procedure from my nhibernate code passing in the appropriate values :)
I also discovered in the process that my mapping class was wrong - as it was putting the wrong values in the mapping table (ie. the specialismid was ending up in the specialismcomboid field !)
Your solution should actually work well. The specialisms are filtered by id and there shouldn't be anything left that is not searched for, so count should work. Unless you have the same specialism joined more the once. This currentCriteria lastCriteria stuff looks a bit strange, may be there is an error. Just use Expression.In or Conjunction.
IProjection IdCount = Projections.Count("s.SpecialismId").As("IdCount");
IQuery query = session
.CreateCriteria<SpecialismCombo>("sc")
.CreateCriteria("Specialism", "s");
.Add(Expression.In("s.SpecialismId", SpecialismIds));
.SetProjection(
Projections.GroupProperty("sc.SpecialismComboId"),
IdCount);
.Add(Restrictions.Eq(IdCount, SpecialismIds.Count()));
Should result in a query like this:
select ...
from
SpecialismCombo sc
inner join -- linktable ...
inner join Specialism s on ...
where
s.SpecialismId in (1, 4)
Group By sc.SpecialismComboId
having count(*) = 2
The same in HQL
from SpecialismCombo sc
join sc.Specialism s
where s.id in (:ids)
group by sc
having count(*) = :numberOfIds
You could also join the specialism as many times as you have ids to find:
IQuery query = session.CreateCriteria<SpecialismCombo>("sc")
int counter = 0;
foreach(int id in ids)
{
string alias = "s" + counter++;
query
.CreateCriteria("Specialism", alias);
.Add(Expression.Eq(alias + ".SpecialismId", id));
}
should create a query like this:
select ...
from
SpecialismCombo sc
inner join -- linktable ...
inner join Specialism s0 on ...
inner join -- linktable ...
inner join Specialism s1 on ...
where
s0.SpecialismId = 1
and s1.SpecialismId = 4
So I ended up creating a stored proc and using SQL CTE in order to get only the specialism combos with the correct count of specialisms. Posting this in case someone else comes across a similar issue.
Rediscovered after 8 months of using nhibernate that I'd forgotten a lot of SQL stuff :)
DECLARE #IdCollectionCount INT
, #IdCollection VARCHAR(250)
, #CollectionDelimiter NVARCHAR
SET #IdCollectionCount = 2;
SET #IdCollection = '1,4';
SET #CollectionDelimiter= ',';
WITH CustomQuery AS
(
SELECT sc.SpecialismComboId,
count(s.SpecialismId) AS ItemCount
FROM SpecialismCombo sc
inner join SpecialismComboSpecialism scs on sc.SpecialismComboId = scs.SpecialismComboId
inner join Specialism s on s.SpecialismId = scs.SpecialismId
GROUP BY sc.SpecialismComboId
HAVING count(s.SpecialismId) = #IdCollectionCount
)
SELECT Top 1 CustomQuery.SpecialismComboId FROM CustomQuery
INNER JOIN SpecialismComboSpecialism scs on CustomQuery.SpecialismComboId = scs.SpecialismComboId
INNER JOIN dbo.fn_SplitDelimited(#IdCollection,#CollectionDelimiter) AS ids
ON scs.SpecialismId = CAST(ids.ListValue AS INT)
GROUP BY CustomQuery.SpecialismComboId
HAVING count(scs.SpecialismId) = #IdCollectionCount

LINQ to SQL using GROUP BY and COUNT(DISTINCT)

I have to perform the following SQL query:
select answer_nbr, count(distinct user_nbr)
from tpoll_answer
where poll_nbr = 16
group by answer_nbr
The LINQ to SQL query
from a in tpoll_answer
where a.poll_nbr = 16 select a.answer_nbr, a.user_nbr distinct
maps to the following SQL query:
select distinct answer_nbr, distinct user_nbr
from tpoll_answer
where poll_nbr = 16
So far, so good. However the problem raises when trying to GROUP the results, as I'm not being able to find a LINQ to SQL query that maps to the first query I wrote here (thank you LINQPad for making this process a lot easier). The following is the only one that I've found that gives me the desired result:
from answer in tpoll_answer where answer.poll_nbr = 16 _
group by a_id = answer.answer_nbr into votes = count(answer.user_nbr)
Which in turns produces the follwing ugly and non-optimized at all SQL query:
SELECT [t1].[answer_nbr] AS [a_id], (
SELECT COUNT(*)
FROM (
SELECT CONVERT(Bit,[t2].[user_nbr]) AS [value], [t2].[answer_nbr], [t2].[poll_nbr]
FROM [TPOLL_ANSWER] AS [t2]
) AS [t3]
WHERE ([t3].[value] = 1) AND ([t1].[answer_nbr] = [t3].[answer_nbr]) AND ([t3].[poll_nbr] = #p0)
) AS [votes]
FROM (
SELECT [t0].[answer_nbr]
FROM [TPOLL_ANSWER] AS [t0]
WHERE [t0].[poll_nbr] = #p0
GROUP BY [t0].[answer_nbr]
) AS [t1]
-- #p0: Input Int (Size = 0; Prec = 0; Scale = 0) [16]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.1
Any help will be more than appreciated.
There isn't direct support for COUNT(DISTINCT {x})), but you can simulate it from an IGrouping<,> (i.e. what group by returns); I'm afraid I only "do" C#, so you'll have to translate to VB...
select new
{
Foo= grp.Key,
Bar= grp.Select(x => x.SomeField).Distinct().Count()
};
Here's a Northwind example:
using(var ctx = new DataClasses1DataContext())
{
ctx.Log = Console.Out; // log TSQL to console
var qry = from cust in ctx.Customers
where cust.CustomerID != ""
group cust by cust.Country
into grp
select new
{
Country = grp.Key,
Count = grp.Select(x => x.City).Distinct().Count()
};
foreach(var row in qry.OrderBy(x=>x.Country))
{
Console.WriteLine("{0}: {1}", row.Country, row.Count);
}
}
The TSQL isn't quite what we'd like, but it does the job:
SELECT [t1].[Country], (
SELECT COUNT(*)
FROM (
SELECT DISTINCT [t2].[City]
FROM [dbo].[Customers] AS [t2]
WHERE ((([t1].[Country] IS NULL) AND ([t2].[Country] IS NULL)) OR (([t1]
.[Country] IS NOT NULL) AND ([t2].[Country] IS NOT NULL) AND ([t1].[Country] = [
t2].[Country]))) AND ([t2].[CustomerID] <> #p0)
) AS [t3]
) AS [Count]
FROM (
SELECT [t0].[Country]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[CustomerID] <> #p0
GROUP BY [t0].[Country]
) AS [t1]
-- #p0: Input NVarChar (Size = 0; Prec = 0; Scale = 0) []
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.1
The results, however, are correct- verifyable by running it manually:
const string sql = #"
SELECT c.Country, COUNT(DISTINCT c.City) AS [Count]
FROM Customers c
WHERE c.CustomerID != ''
GROUP BY c.Country
ORDER BY c.Country";
var qry2 = ctx.ExecuteQuery<QueryResult>(sql);
foreach(var row in qry2)
{
Console.WriteLine("{0}: {1}", row.Country, row.Count);
}
With definition:
class QueryResult
{
public string Country { get; set; }
public int Count { get; set; }
}
The Northwind example cited by Marc Gravell can be rewritten with the City column selected directly by the group statement:
from cust in ctx.Customers
where cust.CustomerID != ""
group cust.City /*here*/ by cust.Country
into grp
select new
{
Country = grp.Key,
Count = grp.Distinct().Count()
};
Linq to sql has no support for Count(Distinct ...). You therefore have to map a .NET method in code onto a Sql server function (thus Count(distinct.. )) and use that.
btw, it doesn't help if you post pseudo code copied from a toolkit in a format that's neither VB.NET nor C#.
This is how you do a distinct count query. Note that you have to filter out the nulls.
var useranswercount = (from a in tpoll_answer
where user_nbr != null && answer_nbr != null
select user_nbr).Distinct().Count();
If you combine this with into your current grouping code, I think you'll have your solution.
simple and clean example of how group by works in LINQ
http://www.a2zmenu.com/LINQ/LINQ-to-SQL-Group-By-Operator.aspx
I wouldn't bother doing it in Linq2SQL. Create a stored Procedure for the query you want and understand and then create the object to the stored procedure in the framework or just connect direct to it.

Categories