59 votes

LINQ to SQL à l'aide de GROUPE PAR et COUNT(DISTINCT)

J'ai pour exécuter la requête SQL suivante:

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

Le LINQ to SQL de la requête

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

les cartes de la requête SQL suivante:

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

Pour l'instant, donc bon. Cependant, le problème soulève lors de la tentative de regrouper les résultats, que je suis de ne pas être en mesure de trouver un LINQ to SQL de la requête qui correspond à la première requête que j'ai écrit ici (merci LINQPad pour rendre ce processus beaucoup plus facile). Ce qui suit est le seul que j'ai trouvé qui me donne le résultat souhaité:

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

Qui à son tour produit de l'follwing laid et non optimisé à tous les requête 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

Toute aide sera plus apprécié.

90voto

Marc Gravell Points 482669

Il n'y a pas de support direct pour COUNT(DISTINCT {x})), mais vous pouvez le simuler à partir d'un IGrouping<,> (c'est à dire qu' group by des retours); j'ai peur qu'à "faire" de C#, de sorte que vous aurez à traduire de VB...

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

Voici un exemple 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);
        }
    }

Le TSQL n'est pas tout à fait ce que nous aimerions, mais il fait le travail:

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

Les résultats, cependant, sont corrects - verifyable en exécutant manuellement:

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

Avec la définition:

class QueryResult
{
    public string Country { get; set; }
    public int Count { get; set; }
}

10voto

Alex Kamburov Points 96

Les Comptoirs exemple cité par Marc Gravel peut être réécrite avec la Ville de la colonne sélectionnée directement par la déclaration du groupe:

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

1voto

Frans Bouma Points 6015

Linq to sql n'a pas de support pour Count(Distinct ...). Par conséquent, vous devez mapper un .NET de la méthode dans le code sur une fonction Sql server (donc Count(distinct.. )) et l'utiliser.

btw, il n'aide pas si vous publiez des pseudo-code copié à partir d'une boîte à outils dans un format qui n'est ni VB.NET ni C#.

1voto

GeekyMonkey Points 5036

C'est une façon de faire un comptage de la requête. Notez que vous devez filtrer les valeurs null.

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

Si vous combinez cela avec dans votre code de regroupement, je pense que vous aurez votre solution.

1voto

rs.emenu Points 27

simple et propre exemple de la façon dont le groupe dans les œuvres de LINQ

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X