c# - 如何在 Entity Framework 中使用条件进行快速 GroupBy 查询

标签 c# sql-server performance entity-framework linq-to-sql

我在使用 GROUP BY 时遇到严重的性能问题和 WHERE在 Entity Framework 中。

这是一个查询 Northwind Database 的示例:

from order in Orders
join detail in OrderDetails on order.OrderID equals detail.OrderID
group detail by order.OrderDate into dateGroup
select new
{
    dateGroup.Key,
    Foo = dateGroup.Where(e => e.ProductID > 20).Sum(e => (decimal?)e.UnitPrice) ?? 0,
    Bar = dateGroup.Where(e => e.ProductID > 40).Sum(e => (decimal?)e.UnitPrice) ?? 0,
    Baz = dateGroup.Where(e => e.ProductID > 60).Sum(e => (decimal?)e.UnitPrice) ?? 0
}

这会生成以下 sql:

-- Region Parameters
DECLARE @p0 Int = 20
DECLARE @p1 Decimal(5,4) = 0
DECLARE @p2 Int = 40
DECLARE @p3 Decimal(5,4) = 0
DECLARE @p4 Int = 60
DECLARE @p5 Decimal(5,4) = 0
-- EndRegion
SELECT [t2].[OrderDate] AS [Key], COALESCE((
    SELECT SUM([t5].[value])
    FROM (
        SELECT [t4].[UnitPrice] AS [value], [t4].[ProductID], [t3].[OrderDate]
        FROM [Orders] AS [t3]
        INNER JOIN [Order Details] AS [t4] ON [t3].[OrderID] = [t4].[OrderID]
        ) AS [t5]
    WHERE ([t5].[ProductID] > @p0) AND ((([t2].[OrderDate] IS NULL) AND ([t5].[OrderDate] IS NULL)) OR (([t2].[OrderDate] IS NOT NULL) AND ([t5].[OrderDate] IS NOT NULL) AND ([t2].[OrderDate] = [t5].[OrderDate])))
    ),@p1) AS [Foo], COALESCE((
    SELECT SUM([t8].[value])
    FROM (
        SELECT [t7].[UnitPrice] AS [value], [t7].[ProductID], [t6].[OrderDate]
        FROM [Orders] AS [t6]
        INNER JOIN [Order Details] AS [t7] ON [t6].[OrderID] = [t7].[OrderID]
        ) AS [t8]
    WHERE ([t8].[ProductID] > @p2) AND ((([t2].[OrderDate] IS NULL) AND ([t8].[OrderDate] IS NULL)) OR (([t2].[OrderDate] IS NOT NULL) AND ([t8].[OrderDate] IS NOT NULL) AND ([t2].[OrderDate] = [t8].[OrderDate])))
    ),@p3) AS [Bar], COALESCE((
    SELECT SUM([t11].[value])
    FROM (
        SELECT [t10].[UnitPrice] AS [value], [t10].[ProductID], [t9].[OrderDate]
        FROM [Orders] AS [t9]
        INNER JOIN [Order Details] AS [t10] ON [t9].[OrderID] = [t10].[OrderID]
        ) AS [t11]
    WHERE ([t11].[ProductID] > @p4) AND ((([t2].[OrderDate] IS NULL) AND ([t11].[OrderDate] IS NULL)) OR (([t2].[OrderDate] IS NOT NULL) AND ([t11].[OrderDate] IS NOT NULL) AND ([t2].[OrderDate] = [t11].[OrderDate])))
    ),@p5) AS [Baz]
FROM (
    SELECT [t0].[OrderDate]
    FROM [Orders] AS [t0]
    INNER JOIN [Order Details] AS [t1] ON [t0].[OrderID] = [t1].[OrderID]
    GROUP BY [t0].[OrderDate]
    ) AS [t2]

如您所见,Foo , BarBaz作为单独的子查询执行。每个子查询 selectjoin再次。

我期待的是这样的:

SELECT
  Orders.OrderDate,
  SUM(
    CASE
      WHEN [Order Details].ProductID > 20
      THEN [Order Details].UnitPrice
      ELSE 0
    END
  ) as Foo,
  SUM(
    CASE
      WHEN [Order Details].ProductID > 40
      THEN [Order Details].UnitPrice
      ELSE 0
    END
  ) as Bar,
  SUM(
    CASE
      WHEN [Order Details].ProductID > 60
      THEN [Order Details].UnitPrice
      ELSE 0
    END
  ) as Baz
FROM Orders
JOIN [Order Details] ON Orders.OrderID = [Order Details].OrderID
GROUP BY Orders.OrderDate

是否有可能让底层 linq 提供程序在不使用 ctx.Database.SqlQuery<T> 的情况下为这种情况生成更好的 SQL? ?

在我的真实场景中,我们讨论的是 7 个连接、嵌套分组依据以及更多条件。 EF需要180秒,SQL需要3秒。

最佳答案

如果您想在 LINQ to Entities (EF6) 中获得良好的 SQL 转换,请避免对 GroupBy 运算符的结果使用 Where。尝试使用等效的条件聚合,例如:

Foo = dateGroup.Sum(e => e.ProductID > 20 ? (decimal?)e.UnitPrice : null) ?? 0,
Bar = dateGroup.Sum(e => e.ProductID > 40 ? (decimal?)e.UnitPrice : null) ?? 0,
Baz = dateGroup.Sum(e => e.ProductID > 60 ? (decimal?)e.UnitPrice : null) ?? 0

关于c# - 如何在 Entity Framework 中使用条件进行快速 GroupBy 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40977205/

相关文章:

c# - 如何在 Blazor 中绑定(bind)单选按钮?

c# - 使用 OpenXml 插入 SQL

ruby-on-rails - 如何加快 Rails 延迟作业的启动时间?

sql-server - 我可以在 Sql Server 中对一组数字执行按位和运算吗?

sql - 为什么我不能在SQL Server的CASE语句列上使用filter我的WHERE子句?

performance - 什么会干扰测试服务器的性能?

ios - 在将大型 UIImage 绘制到其中后,CGContextDrawImage 非常慢

c# - MonoTouch.Dialog:具有值的 StringElement,但在 UI 中隐藏值?

c# - 在服务器上搜索文件名

c# - 什么是解构编译器? C# 如何获得动态语言在运行时通过 Roslyn 生成和调用代码的能力?