【问题标题】:HAVING clause in EF LinqEF Linq 中的 HAVING 子句
【发布时间】:2018-10-11 20:17:15
【问题描述】:

我想获取客户 ID 列表和该客户下的订单数量。过滤条件为:

  1. 总额不超过 10 美元的订单将不计算在内。
  2. 未下至少 3 个订单(每个订单总额为 10 美元或更多)的客户将不会被列出。

所以,我会在 SQL 中执行以下操作:

SELECT customerID, COUNT(*)
FROM Orders
WHERE orderTotal > 10
GROUP BY customerID
HAVING COUNT(*) > 2

在 EF 中,我认为这可以表示为:

dbContext.Order
.Where(o => o.orderTotal > 10)
.GroupBy(o => o.customerID)
.Where(g => g.Count() > 2)
.ToList();

但这会产生以下 SQL,它使用派生表和连接,而不是简单地使用 HAVING 子句。我认为这在性能方面远非最佳。是否有更好的方法在 EF 中制定案例,以便翻译后的查询将使用 HAVING 子句?

SELECT 
    [Project1].[C1] AS [C1], 
    [Project1].[customerID] AS [customerID], 
    [Project1].[C2] AS [C2], 
    [Project1].[ID] AS [ID], 

FROM ( SELECT 
    [GroupBy1].[K1] AS [customerID], 
    1 AS [C1], 
    [Extent2].[ID] AS [ID], 
    CASE WHEN ([Extent2].[storeID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
    FROM   (SELECT 
        [Extent1].[customerID] AS [K1], 
        COUNT(1) AS [A1]
        FROM [dbo].[Orders] AS [Extent1]
        WHERE [Extent1].[orderTotal] > cast(10 as decimal(18))
        GROUP BY [Extent1].[customerID] ) AS [GroupBy1]
    LEFT OUTER JOIN [dbo].[Orders] AS [Extent2] ON ([Extent2].[orderTotal] > cast(10 as decimal(18))) AND (([GroupBy1].[K1] = [Extent2].[customerID]) OR (([GroupBy1].[K1] IS NULL) AND ([Extent2].[customerID] IS NULL)))
    WHERE [GroupBy1].[A1] > 2
)  AS [Project1]
ORDER BY [Project1].[customerID] ASC, [Project1].[C2] ASC

【问题讨论】:

  • 您是遇到性能问题还是只是猜测?

标签: c# sql sql-server entity-framework linq


【解决方案1】:

嗯,LINQ to Entities 查询不等同于 SQL 查询,因为它返回一个 groupings(键和匹配元素对)列表,根本没有 SQL 等效项。

如果您在 SQL 查询中只返回 customerId 和 Count:

db.Orders
.Where(o => o.orderTotal > 10)
.GroupBy(o => o.customerID)
.Select(g => new { customerId = g.Key, orderCount = g.Count() })
.Where(g => g.Count > 2)
.ToList();

那么 EF 生成的 SQL 将与预期的几乎相同(或功能等效):

SELECT
    [GroupBy1].[K1] AS [customerID],
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT
        [Extent1].[customerID] AS [K1],
        COUNT(1) AS [A1]
        FROM [dbo].[Orders] AS [Extent1]
        WHERE [Extent1].[orderTotal] > cast(10 as decimal(18))
        GROUP BY [Extent1].[customerID]
    )  AS [GroupBy1]
    WHERE [GroupBy1].[A1] > 2

【讨论】:

  • 所以它永远不会产生带有 HAVING 子句的 SQL?
  • @JohnL。如果您的 Linq 查询最终没有返回每个 IGrouping 中的数据并且 only 使用聚合(但此答案中的 Linq 查询 确实,我相信它将使用 HAVING返回所有内部数据)。请注意,使用WHERE 的外部查询(很大程度上)是等效的,如果与使用HAVING 的手写查询相比,DBMS 提出了完全不同的执行计划,我会感到惊讶。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多