LINQ to SQL с использованием GROUP BY и COUNT (DISTINCT)
Мне нужно выполнить следующий SQL-запрос:
select answer_nbr, count(distinct user_nbr)
from tpoll_answer
where poll_nbr = 16
group by answer_nbr
Запрос LINQ to SQL
from a in tpoll_answer
where a.poll_nbr = 16 select a.answer_nbr, a.user_nbr distinct
отображает следующий SQL-запрос:
select distinct answer_nbr, distinct user_nbr
from tpoll_answer
where poll_nbr = 16
До сих пор так хорошо. Однако проблема возникает при попытке GROUP для результатов, поскольку я не могу найти запрос LINQ to SQL, который сопоставляется с первым запросом, который я написал здесь (спасибо LINQPad, чтобы сделать этот процесс намного проще). Единственное, что я нашел, это следующее:
from answer in tpoll_answer where answer.poll_nbr = 16 _
group by a_id = answer.answer_nbr into votes = count(answer.user_nbr)
Который по очереди производит безобразное и не оптимизированное во всех SQL-запросах следующее:
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
Любая помощь будет более чем оценена.
Ответы
Ответ 1
Существует прямая поддержка COUNT(DISTINCT {x}))
, но вы можете имитировать ее из IGrouping<,>
(то есть, что возвращает group by
); Боюсь, что я только "делаю" С#, поэтому вам придется перевести на VB...
select new
{
Foo= grp.Key,
Bar= grp.Select(x => x.SomeField).Distinct().Count()
};
Здесь пример Northwind:
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);
}
}
TSQL не совсем то, что нам хотелось бы, но он выполняет эту работу:
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
Результаты, однако, корректно проверяются путем его запуска вручную:
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);
}
С определением:
class QueryResult
{
public string Country { get; set; }
public int Count { get; set; }
}
Ответ 2
Пример Northwind, приведенный Марк Гравелл, можно переписать с помощью столбца City, выбранного непосредственно в заявлении группы:
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()
};
Ответ 3
Linq to sql не поддерживает Count (Distinct...). Поэтому вам необходимо сопоставить метод .NET в коде на серверную функцию Sql (таким образом, Count (отчетный)) и использовать это.
btw, это не поможет, если вы публикуете псевдокод, скопированный из инструментария в формате, который ни VB.NET, ни С#.
Ответ 4
Вот как вы делаете отдельный запрос счетчика. Обратите внимание, что вам нужно отфильтровать нули.
var useranswercount = (from a in tpoll_answer
where user_nbr != null && answer_nbr != null
select user_nbr).Distinct().Count();
Если вы соедините это с вашим текущим кодом группировки, я думаю, что у вас будет ваше решение.
Ответ 5
простой и чистый пример того, как группа работает в LINQ
http://www.a2zmenu.com/LINQ/LINQ-to-SQL-Group-By-Operator.aspx
Ответ 6
Я бы не стал делать это в Linq2SQL. Создайте хранимую процедуру для запроса, который вы хотите и понимаете, а затем создайте объект для хранимой процедуры в рамках или просто подключитесь к нему напрямую.