LINQ a SQL usando GROUP BY y COUNT (DISTINTO)


Tengo que realizar la siguiente consulta SQL:

select answer_nbr, count(distinct user_nbr)
from tpoll_answer
where poll_nbr = 16
group by answer_nbr

La consulta LINQ a SQL

from a in tpoll_answer 
where a.poll_nbr = 16 select a.answer_nbr, a.user_nbr distinct 

Se asigna a la siguiente consulta SQL:

select distinct answer_nbr, distinct user_nbr
from tpoll_answer
where poll_nbr = 16

Hasta ahora, todo bien. Sin embargo, el problema surge al intentar AGRUPAR los resultados, ya que no estoy siendo capaz de encontrar una consulta de LINQ a SQL que se corresponda con la primera consulta que escribí aquí (gracias LINQPad por hacer este proceso mucho más fácil). El siguiente es el único que he encontrado que me da el deseado resultado:

from answer in tpoll_answer where answer.poll_nbr = 16 _
group by a_id = answer.answer_nbr into votes = count(answer.user_nbr)

Que a su vez produce la siguiente consulta SQL fea y no optimizada:

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

Cualquier ayuda será más que apreciada.

Author: Brian Webster, 2009-01-15

6 answers

No hay soporte directo para COUNT(DISTINCT {x})), pero se puede simular desde un IGrouping<,> (es decir, lo que group by devuelve); me temo que solo "hago" C#, por lo que tendrá que traducir a VB...

 select new
 {
     Foo= grp.Key,
     Bar= grp.Select(x => x.SomeField).Distinct().Count()
 };

Aquí hay un ejemplo de 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);
        }
    }

El TSQL no es exactamente lo que nos gustaría, pero hace el trabajo:

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

Los resultados, sin embargo, son verificables correctamente ejecutándolo manualmente:

        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);
        }

Con definición:

class QueryResult
{
    public string Country { get; set; }
    public int Count { get; set; }
}
 84
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-16 09:51:02

El ejemplo Northwind citado por Marc Gravell se puede reescribir con la columna City seleccionada directamente por la instrucción group:

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()
};
 10
Author: Alex Kamburov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-04 21:38:49

Linq to sql no tiene soporte para Count(Distinct ...). Por lo tanto, debe asignar un método.NET en código a una función de Sql server (por lo tanto, Count(distinct.. )) y usa eso.

Por cierto, no ayuda si publicas pseudo código copiado de un kit de herramientas en un formato que no sea ninguno VB.NET ni C#.

 1
Author: Frans Bouma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-16 08:31:04

Así es como se hace una consulta de conteo distinta. Ten en cuenta que tienes que filtrar los nulos.

var useranswercount = (from a in tpoll_answer
where user_nbr != null && answer_nbr != null
select user_nbr).Distinct().Count();

Si combina esto con en su código de agrupación actual, creo que tendrá su solución.

 1
Author: GeekyMonkey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-16 08:58:08

Ejemplo simple y limpio de cómo funciona group by en LINQ

Http://www.a2zmenu.com/LINQ/LINQ-to-SQL-Group-By-Operator.aspx

 1
Author: rs.emenu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-25 11:09:35

No me molestaría en hacerlo en Linq2Sql. Cree un procedimiento almacenado para la consulta que desea y entienda y luego cree el objeto para el procedimiento almacenado en el marco o simplemente conéctese directamente a él.

 -4
Author: user6504946,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-23 15:01:01