【问题标题】:LINQ to SQL using GROUP BY and COUNT(DISTINCT)使用 GROUP BY 和 COUNT(DISTINCT) 的 LINQ to SQL
【发布时间】:2009-01-15 19:51:38
【问题描述】:

我必须执行以下 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

任何帮助将不胜感激。

【问题讨论】:

    标签: c# linq linq-to-sql


    【解决方案1】:

    没有对COUNT(DISTINCT {x})) 的直接支持,但您可以从IGrouping<,> 模拟它(即group by 返回的内容);恐怕我只会“做”C#,所以你必须翻译成 VB...

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

    这是一个罗斯文的例子:

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

    【讨论】:

    • 谢谢你,马克。是的,我也考虑过这种方法,实际上是一个非常相似的查询,可以返回正确的结果。遗憾的是,生成的 SQL 不是最佳的,但它可以工作,暂时就足够了。
    • @Leandro - 它可能是更多的 TSQL,但我会在声明任何关于最佳的之前比较实际的查询计划 - 它很可能与优化器相同。
    • 如果 sql 很垃圾,为什么不先用普通 sql 写呢?
    • 这太好了,谢谢!这正是我的 POCO 集合所需要的 :-) 我没有使用 SQL 后端,所以纯 SQL 不是我的选择。
    • 仅供参考,现在完全支持 distinct。
    【解决方案2】:

    Marc Gravell 引用的 Northwind 示例可以用 group 语句直接选择的 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(distinct.. ))并使用它。

      顺便说一句,如果您以既不是 VB.NET 也不是 C# 的格式发布从工具包复制的伪代码,这将无济于事。

      【讨论】:

      • 谢谢 Frans,我正在考虑创建视图或存储过程。非常感谢马克。
      • @Leandro - 您也可以考虑使用表值函数 (UDF) 代替存储过程;系统可以更准确地获取元数据,并且可以在服务器端进行组合。仅适用于 LINQ-to-SQL,但不适用于实体框架 (AFAIK)。
      【解决方案4】:

      这就是您执行不同计数查询的方式。请注意,您必须过滤掉空值。

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

      如果你把它与你当前的分组代码结合起来,我想你会有你的解决方案。

      【讨论】:

      • 谢谢 GeekyMonkey。列不是 NULL,因此无需担心。如果我没记错的话,我认为您的查询是总计数而不是按计数分组。
      【解决方案5】:

      在 LINQ 中 group by 如何工作的简单而干净的示例

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

      【讨论】:

      • 抱歉,没有找到关于 count(distinct) 的信息
      • packages.GroupBy(p =&gt; p.OrderId).Count()
      【解决方案6】:

      我不会费心在 Linq2SQL 中这样做。为您想要和理解的查询创建一个存储过程,然后在框架中为存储过程创建对象,或者直接连接到它。

      【讨论】:

        猜你喜欢
        • 2011-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-07
        • 1970-01-01
        相关资源
        最近更新 更多