web-dev-qa-db-fra.com

LINQ to SQL en utilisant GROUP BY et COUNT (DISTINCT)

Je dois effectuer la requête SQL suivante:

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

La requête LINQ to SQL

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

mappe à la requête SQL suivante:

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

Jusqu'ici tout va bien. Cependant, le problème se pose lorsque vous essayez de regrouper les résultats, car je ne parviens pas à trouver une requête LINQ to SQL mappée sur la première requête que j'ai écrite ici (merci LINQPad pour avoir rendu ce processus une 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)

Ce qui à son tour produit la requête laide et non optimisée suivante pour toutes les requêtes 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 qu'appréciée.

58
Leandro López

Il n'y a pas de support direct pour COUNT(DISTINCT {x})), mais vous pouvez le simuler à partir d'un IGrouping<,> (c'est-à-dire ce que group by résultats); J'ai bien peur de ne "faire" que C #, vous devrez donc traduire en VB ...

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

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

TSQL n’est pas 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 et vérifiables en l'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 définition:

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

L’exemple de Northwind cité par Marc Gravell peut être réécrit avec la colonne Ville sélectionnée directement par la déclaration de 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()
};
10
Alex Kamburov

Linq to sql n'a pas de support pour Count (Distinct ...). Vous devez donc mapper une méthode .NET en code sur une fonction serveur Sql (donc Count (distinct ..)) et l'utiliser.

au fait, ça n'aide pas si vous postez un pseudo-code copié depuis une boîte à outils dans un format qui n'est ni VB.NET ni C #.

1
Frans Bouma

Voici comment vous faites une requête de compte distinct. 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 votre code de regroupement actuel, je pense que vous aurez votre solution.

1
GeekyMonkey

exemple simple et net du fonctionnement de group by dans LINQ

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

1
rs.emenu