【问题标题】:Using Linq to SQL, how do I find min and max of a column in a table?使用 Linq to SQL,如何找到表中列的最小值和最大值?
【发布时间】:2011-01-17 02:41:40
【问题描述】:

我想找到最快的方法来获取表中的列的最小值和最大值,只需一次 Linq 到 SQL 往返。所以我知道这将在两次往返中起作用:

int min = MyTable.Min(row => row.FavoriteNumber);
int max = MyTable.Max(row => row.FavoriteNumber);

我知道我可以使用group,但我没有group by 子句,我想汇总整个表格!如果不先分组,我就不能使用 .Min 。我确实尝试过:

from row in MyTable 
group row by true into r 
select new { 
    min = r.Min(z => z.FavoriteNumber), 
    max = r.Max(z => z.FavoriteNumber) 
}

但是那个疯狂的 group 子句看起来很傻,而且它产生的 SQL 比它需要的复杂。

那么,有什么办法可以得到正确的 SQL 呢?

编辑:这些人也失败了:Linq to SQL: how to aggregate without a group by? ...如果真的没有答案,LINQ 设计人员的疏忽监督。

编辑 2:我在 SQL Server Management Studio 执行计划分析中查看了我自己的解决方案(使用无意义的常量 group by 子句),在我看来它与以下生成的计划相同:

SELECT MIN(FavoriteNumber), MAX(FavoriteNumber)
FROM MyTable

所以除非有人能想出一个更简单或同样好的答案,否则我认为我必须将其标记为自己回答。想法?

【问题讨论】:

  • 为什么要求单次往返?
  • 效率和整洁——看起来应该很简单,SQL也很简单:SELECT MIN(FavoriteNumber), MAX(FavoriteNumber) FROM MyTable... 所以LINQ也应该如此。
  • 查看 SQL LINQ 输出内容的工具:ayende.com/Blog/archive/2010/02/20/…
  • 我也大量使用过LinqPad,它也显示SQL,非常好用。
  • “它生成的 SQL 比它需要的更复杂” - 这不是 LINQ 的问题,而是分析您的 linq 表达式并将其转换为 SQL 的底层 LINQ 提供程序。 LINQ 只定义约束和结果。如果 Provider 正确分析并减少了查询,那么生成的 SQL 将只是“从 X 中选择 min() max()”,就像计划分析器向您展示的那样。如果您遇到笨拙的 SQL,请考虑更改您的提供程序库或将其更新到新版本:) 无论如何,按常量分组是表达您所需查询的绝对有效和正确的方式。

标签: c# sql linq-to-sql


【解决方案1】:

如问题中所述,这种方法似乎实际上生成了最佳 SQL 代码,因此虽然它在 LINQ 中看起来有点怪异,但它应该是最佳性能。

from row in MyTable  
group row by true into r  
select new {  
    min = r.Min(z => z.FavoriteNumber),  
    max = r.Max(z => z.FavoriteNumber)  
} 

【讨论】:

    【解决方案2】:

    与 select min(val), max(val) from table 相比,我只能找到这个产生一些干净的 sql 的语句仍然不是很有效:

    var r =
      (from min in items.OrderBy(i => i.Value)
       from max in items.OrderByDescending(i => i.Value)
       select new {min, max}).First();
    

    sql是

    SELECT TOP (1)
        [t0].[Value],
        [t1].[Value] AS [Value2]
    FROM
        [TestTable] AS [t0],
        [TestTable] AS [t1]
    ORDER BY
        [t0].[Value],
        [t1].[Value] DESC
    

    还有另一种选择可以对最小和最大查询使用单个连接(请参阅Multiple Active Result Sets (MARS)

    或存储过程..

    【讨论】:

    • 创意最高分!不过,我在 SQL 分析器中尝试过,而且……不漂亮。它要做的定制订购工作显然非常痛苦。
    【解决方案3】:

    我还不确定如何将它翻译成 C#(我正在努力)

    这是 Haskell 版本

    minAndMax :: Ord a => [a] -> (a,a)
    minAndMax [x]    = (x,x)
    minAndMax (x:xs) = (min a x, max b x)
                       where (a,b) = minAndMax xs
    

    C# 版本应该以某种方式涉及Aggregate(我认为)。

    【讨论】:

    • 我要试试这个好玩:)
    【解决方案4】:

    您可以选择整个表,并在内存中执行最小和最大操作:

    var cache = // select *
    
    var min = cache.Min(...);
    var max = cache.Max(...);
    

    根据您的数据集有多大,这可能是避免多次访问数据库的方法。

    【讨论】:

    • 我投了你一票,因为我认为你也不应该得到-1,因为你达到了既定目标。但是,我使用单次往返的原因是 a) 整洁和 b) 效率,因此拉动整个桌子会伤害到:它不仅必须转移整个桌子,而且还必须做 min 和 max搜索列表两次,而不是使用数据库的索引。
    • 我同意。但如果它是一个小表,它不会像你的代码生成的 SQL 那样受到伤害......性能方面。谢谢:)
    【解决方案5】:

    LINQ to SQL 查询是单个表达式。因此,如果您不能用一个表达式来表达您的查询(或者一旦您不喜欢它),那么您必须考虑其他选项。

    存储过程,因为它们可以有语句,使您能够在一次往返中完成此操作。您将有两个输出参数或选择一个包含两行的结果集。无论哪种方式,您都需要自定义代码来读取存储过程的结果。

    (我个人认为没有必要在这里避免两次往返。这似乎是一个过早的优化,尤其是因为您可能不得不跳过一些环节才能使其正常工作。更不用说您将花费的时间证明这一决定的合理性并向其他开发人员解释解决方案。)

    换一种说法:您已经回答了自己的问题。 “如果不先分组,我就不能使用 .Min”,然后是“那个疯狂的 group 子句看起来很傻,而且它产生的 SQL 比它需要的更复杂”,这些线索表明简单易懂的两个 -往返解决方案是您意图的最佳表达方式(除非您编写自定义 SQL)。

    【讨论】:

    • 确实如此。如果我认为解决方案足够复杂以使用存储过程或导致我直接使用 SQL,那么它就不值得了。我希望有一个简单的语法来使用聚合函数,而不需要我刚刚忽略的分组。但如果没有,我同意额外的往返(可能还有额外的数据库端通行证)不值得太多麻烦。
    猜你喜欢
    • 1970-01-01
    • 2014-12-26
    • 2020-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-22
    相关资源
    最近更新 更多