实体框架SUM CASE未优化

时间:2015-07-16 15:18:44

标签: c# entity-framework

我试图在LinQ中编写一个简单的SQL查询,无论我怎么努力,我总是得到一个复杂的查询。

这是我想要实现的SQL(这不是我得到的):

SELECT
    ClearingAccounts.ID,
    SUM(CASE WHEN Payments.StatusID = 1 THEN Payments.TotalAmount ELSE 0 END) AS Sum1,
    SUM(CASE WHEN DirectDebits.StatusID = 2 THEN DirectDebits.TotalAmount ELSE 0 END) AS Sum2,
    SUM(CASE WHEN Payments.StatusID = 2 THEN Payments.TotalAmount ELSE 0 END) AS Sum3,
    SUM(CASE WHEN DirectDebits.StatusID = 1 THEN DirectDebits.TotalAmount ELSE 0 END) AS Sum4
FROM ClearingAccounts
LEFT JOIN Payments ON Payments.ClearingAccountID = ClearingAccounts.ID
LEFT JOIN DirectDebits ON DirectDebits.ClearingAccountID = ClearingAccounts.ID
GROUP BY ClearingAccounts.ID

以下是代码:

from clearingAccount in clearingAccounts
let payments = clearingAccount.Payments
let directDebits = clearingAccount.DirectDebits
select new
{
    ID = clearingAccount.ID,
    Sum1 = payments.Sum(p => p.StatusID == 1 ? p.TotalAmount : 0),
    Sum2 = directDebits.Sum(p => p.StatusID == 2 ? p.TotalAmount : 0),
    Sum3 = payments.Sum(p => p.StatusID == 2 ? p.TotalAmount : 0),
    Sum4 = directDebits.Sum(p => p.StatusID == 1 ? p.TotalAmount : 0),
}

生成的查询从每个总和的相应表中获取数据,因此四次。我不确定是否可以优化它?

编辑这里是生成的查询:

SELECT 
    [Project5].[ID] AS [ID], 
    [Project5].[C1] AS [C1], 
    [Project5].[C2] AS [C2], 
    [Project5].[C3] AS [C3], 
    [Project5].[C4] AS [C4]
    FROM ( SELECT 
        [Project4].[ID] AS [ID], 
        [Project4].[C1] AS [C1], 
        [Project4].[C2] AS [C2], 
        [Project4].[C3] AS [C3], 
        (SELECT 
            SUM([Filter5].[A1]) AS [A1]
            FROM ( SELECT 
                CASE WHEN (1 = [Extent5].[StatusID]) THEN [Extent5].[TotalAmount] ELSE cast(0 as decimal(18)) END AS [A1]
                FROM [dbo].[DirectDebits] AS [Extent5]
                WHERE [Project4].[ID] = [Extent5].[ClearingAccountID]
            )  AS [Filter5]) AS [C4]
        FROM ( SELECT 
            [Project3].[ID] AS [ID], 
            [Project3].[C1] AS [C1], 
            [Project3].[C2] AS [C2], 
            (SELECT 
                SUM([Filter4].[A1]) AS [A1]
                FROM ( SELECT 
                    CASE WHEN (2 = [Extent4].[StatusID]) THEN [Extent4].[TotalAmount] ELSE cast(0 as decimal(18)) END AS [A1]
                    FROM [dbo].[Payments] AS [Extent4]
                    WHERE [Project3].[ID] = [Extent4].[ClearingAccountID]
                )  AS [Filter4]) AS [C3]
            FROM ( SELECT 
                [Project2].[ID] AS [ID], 
                [Project2].[C1] AS [C1], 
                (SELECT 
                    SUM([Filter3].[A1]) AS [A1]
                    FROM ( SELECT 
                        CASE WHEN (2 = [Extent3].[StatusID]) THEN [Extent3].[TotalAmount] ELSE cast(0 as decimal(18)) END AS [A1]
                        FROM [dbo].[DirectDebits] AS [Extent3]
                        WHERE [Project2].[ID] = [Extent3].[ClearingAccountID]
                    )  AS [Filter3]) AS [C2]
                FROM ( SELECT 
                    [Project1].[ID] AS [ID], 
                    (SELECT 
                        SUM([Filter2].[A1]) AS [A1]
                        FROM ( SELECT 
                            CASE WHEN (1 = [Extent2].[StatusID]) THEN [Extent2].[TotalAmount] ELSE cast(0 as decimal(18)) END AS [A1]
                            FROM [dbo].[Payments] AS [Extent2]
                            WHERE [Project1].[ID] = [Extent2].[ClearingAccountID]
                        )  AS [Filter2]) AS [C1]
                    FROM ( SELECT 
                        [Extent1].[ID] AS [ID]
                        FROM [dbo].[ClearingAccounts] AS [Extent1]
                        WHERE ([Extent1].[CustomerID] = 3) AND ([Extent1].[Deleted] <> 1)
                    )  AS [Project1]
                )  AS [Project2]
            )  AS [Project3]
        )  AS [Project4]
    )  AS [Project5]

2 个答案:

答案 0 :(得分:1)

修改

请注意,根据@ usr的评论,您的原始Sql查询已损坏。通过LEFT OUTER连接两个独立的表,然后对公共连接键进行分组,只要其中一个DirectDebitsPayments表返回多行,就会错误地复制其他&#39;中的TotalAmount值SUMmed colums(反之亦然)。例如如果给定的ClearingAccount有3个DirectDebits和4个Payments,您将获得总共12行(而您应该为这两个表独立地汇总3行和4行)。更好的Sql Query将是:

WITH ctePayments AS
(
  SELECT
      ClearingAccounts.ID,
      -- Note the ELSE 0 projection isn't required as nulls are eliminated from aggregates
      SUM(CASE WHEN Payments.StatusID = 1 THEN Payments.TotalAmount END) AS Sum1,
      SUM(CASE WHEN Payments.StatusID = 2 THEN Payments.TotalAmount END) AS Sum3
  FROM ClearingAccounts
  INNER JOIN Payments ON Payments.ClearingAccountID = ClearingAccounts.ID
  GROUP BY ClearingAccounts.ID
),
cteDirectDebits AS
(
  SELECT
      ClearingAccounts.ID,
      SUM(CASE WHEN DirectDebits.StatusID = 2 THEN DirectDebits.TotalAmount END) AS Sum2,
      SUM(CASE WHEN DirectDebits.StatusID = 1 THEN DirectDebits.TotalAmount END) AS Sum4
  FROM ClearingAccounts
  INNER JOIN DirectDebits ON DirectDebits.ClearingAccountID = ClearingAccounts.ID
  GROUP BY ClearingAccounts.ID
)
SELECT ca.ID, COALESCE(p.Sum1, 0) AS Sum1, COALESCE(d.Sum2, 0) AS Sum2, 
       COALESCE(p.Sum3, 0) AS Sum3, COALESCE(d.Sum4, 0) AS Sum4
FROM
  ClearingAccounts ca
  LEFT OUTER JOIN ctePayments p
    ON ca.ID = p.ID
  LEFT OUTER JOIN cteDirectDebits d
    ON ca.ID = d.ID;
  -- GROUP BY not required, since we have already guaranteed at most one row 
  -- per joined table in the CTE's, assuming ClearingAccounts.ID is unique;

在您考虑转换为LINQ之前,您想要使用测试用例进行修复和测试。

旧答案

Sql构造:

SELECT SUM(CASE WHEN ... THEN 1 ELSE 0 END) AS Something

SELECT列表中应用时,是一种常见的 hack &#39;替代&#39;从更大的数据转移数据选择符合投影标准的列(如果不匹配则选择零)。它根本不是一个总和,它是一个匹配的&#39;计数

关于优化生成的Sql,另一种方法是在加入和分组后实现数据(当然,如果有谓词{{​​1}}子句,也可以通过IQueryable在Sql中应用它),以及然后在内存中进行条件求和:

WHERE

但是,就个人而言,我会将此数据投影直接保留在Sql中,然后使用类似SqlQuery的内容然后从Sql反序列化结果 直接进入最终的实体类型。

答案 1 :(得分:-1)

1) 在EF中添加AsNoTracking以避免跟踪更改

检查您对JOIN使用的列是否有索引。特别是您用于分组的列。描述查询并对其进行优化。 EF还存储过程的开销。

2)如果找不到按照需要快速生成的方法,创建存储过程并从EF 调用它。即使是相同的查询也会更快。