【问题标题】:Max(x=>x.Time) vs OrderByDescending(x=>x.Time).First().TimeMax(x=>x.Time) vs OrderByDescending(x=>x.Time).First().Time
【发布时间】:2011-03-09 12:24:51
【问题描述】:

我提出了一个相关问题:
'Invalid column name [ColumnName]' on a nested linq query

我正在尝试按照此处的要求获得组的第一行:
Linq - Top value from each group

但是,我使用 Max 的方法与接受的答案不同。我是这样做的:

ATable
    .GroupBy (t => t.ID)
    .Select ( t => t.OrderByDescending(x=>x.Timestamp).FirstOrDefault().Timestamp)

这会导致错误SqlException: Invalid column name 'ID'

现在当我使用 Max 函数时:

ATable
    .GroupBy (t => t.ID)
    .Select ( t => t.Max(x=>x.Timestamp))

它返回我想要的时间列表。

我的理解是它们是不同但等效的调用。为什么我在第一次调用时收到 sql 错误?

更新
为第一个查询生成的 sql 查询如下所示:

SELECT (
    SELECT [t3].[Timestamp]
    FROM (
        SELECT TOP 1 [t2].[Timestamp]
        FROM [ATable] AS [t2]
        WHERE (([t1].[ID] IS NULL) AND ([t2].[ID] IS NULL)) OR (([t1].[ID] IS NOT NULL) AND ([t2].[ID] IS NOT NULL) AND ([t1].[ID] = [t2].[ID]))
        ORDER BY [t2].[Timestamp] DESC
        ) AS [t3]
    ) AS [value]
FROM (
    SELECT [t0].[ID]
    FROM [ATable] AS [t0]
    GROUP BY [t0].[ID]
    ) AS [t1]

我设法减少它但保持相同的错误:

SELECT (
    SELECT [t3].[Timestamp]
    FROM (
        SELECT TOP 1 [Timestamp]
        FROM [ATable]
        WHERE [t1].[ID] = [ID]
        ) AS [t3]
    ) AS [value]
FROM (
    SELECT [ID]
    FROM [ATable]
    GROUP BY [ID]
    ) AS [t1]

更新2
一些答案说这些查询在逻辑上是不同的,但是,如果你在北风表上做这样的事情,它会产生相同的结果:

OrderDetails
    .GroupBy(x=>x.ProductID)
    .Select(x=>x.OrderByDescending(y=>y.UnitPrice).FirstOrDefault().UnitPrice).Dump();

OrderDetails
    .GroupBy(x=>x.ProductID)
    .Select(x=>x.Max(y=>y.UnitPrice)).Dump();

因此,我不能接受以下 3 个答案中的任何一个:查询不同,或者 Max 给出的是 Sum 而不是列表的最大值,或者您必须在继续之前将 IQueriable 转换为列表。

【问题讨论】:

  • 你能以某种方式让我为第一个查询生成 SQL 查询吗?服务器日志应该有它们。
  • 作为对所提供答案的回应,如果您点击第二个链接,则会提供(不接受)使用 OrderByDescending 但不链接 .Timestamp 或一般 .[FieldID] 的解决方案。这会返回第一行..但链接字段不起作用(即使使用 FirstOrDefault)。
  • 如果它在没有链接Timestamp 的情况下工作,那么两个查询之间肯定有很大的区别。

标签: c# linq linqpad sqlexception


【解决方案1】:

更新 2:我只想回应一下:

但是,如果你做这样的事情 在北风桌上,它会 产生相同的结果[...]所以我 不能接受 3 个答案中的任何一个 表示查询不同

我并不是要质疑他们应该产生相同的结果。为了更好地说明我的观点,让我提供一个类比。

假设我给你一堆打乱的索引卡,编号从 1 到 50,然后我说:“把这些都按顺序排列。”所以,你把它们整理出来,确保堆从 1 到 50。这需要你一两分钟。 然后我说,“现在把牌堆顶上的牌给我。”这张卡恰好是编号为 50 的卡。

另一方面,假设我首先说的是,“给我这张牌中数字最高的牌。”然后,您要做的就是浏览卡片并找到数字最高的卡片,而无需进行任何排序。 (我的意思是,您可以先对堆进行排序,但这显然比我要求的要多。)

所以我说的是上面两组指令的结果是一样的;然而,它们显然是不同的指令

现在,如果某些实现足够智能,可以将它们转换为相同的(优化的)SQL 查询,我一点也不感到惊讶;我要说的是,由于它们不同的指令,因此如果生成的 SQL 不同也不应该那么令人惊讶(尽管它可能是 不幸的,因此一个版本可能导致错误而另一个版本不会导致错误也就不足为奇了)。


更新:再说一次,我真的不能就 Linq-to-SQL 给你一个详细的答复;但我要指出,虽然Max 理论上应该产生与OrderByDescendingFirstOrDefault 一起产生的相同的结果,但从概念的角度来看它并不完全相同。

考虑 Linq-to-objects。调用Max 将遍历序列并累积最大值。调用OrderByDescending对整个序列进行排序,然后然后排在最前面。

显然,Linq-to-SQL 是另一种动物;然而,这是有道理的,因为这些查询在概念上代表了获得特定结果的不同方法,它们产生的 SQL 很可能(而且,正如您所见,)是不同的。


原答案

我真的不能就 Linq-to-SQL 给你一个详细的答复,但我会指出你的两个替代方案之间存在非常严重的差异(它们等效):

Max(x => x.Timestamp)

上面应该返回最大的时间戳

OrderByDescending(x => x.Timestamp()).FirstOrDefault()

上面应该返回时间戳最大的记录

这些是非常不同的野兽。

【讨论】:

  • 本质上两者是一样的,他可以在FirstOrDefault调用之后简单地链接.Timestamp,问题应该是在将表达式树翻译成sql语句。
  • 我的错,我实际上是在链接.Timestamp 来获取错误,仅仅获取 FirstOrDefault() 不会给我错误。现在更新问题。
  • @Femaref:他可以做到这一点(也许他做到了);我只是想我应该指出这一点,以防 OP 对 Max 所做的事情有错误的印象(我知道我错误地认为它将返回记录 with max之前不止一次)。
  • @Joe:感谢您澄清这一点。我还有一个额外的观察;那我就上路了……
  • 是的,我有时也会上当。需要MaxBy运营商。
猜你喜欢
  • 2012-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-23
  • 1970-01-01
相关资源
最近更新 更多