SQL CE 上的 LINQ 聚合左连接

Posted

技术标签:

【中文标题】SQL CE 上的 LINQ 聚合左连接【英文标题】:LINQ aggregate left join on SQL CE 【发布时间】:2009-08-15 17:35:48 【问题描述】:

我需要的是一个如此简单、简单的查询,它让我大吃一惊,我只是在 LINQ 中完成了多少工作。在 T-SQL 中,它将是:

SELECT I.InvoiceID, I.CustomerID, I.Amount AS AmountInvoiced,
       I.Date AS InvoiceDate, ISNULL(SUM(P.Amount), 0) AS AmountPaid,
       I.Amount - ISNULL(SUM(P.Amount), 0) AS AmountDue
FROM Invoices I
LEFT JOIN Payments P ON I.InvoiceID = P.InvoiceID
WHERE I.Date between @start and @end
GROUP BY I.InvoiceID, I.CustomerID, I.Amount, I.Date
ORDER BY AmountDue DESC

我想出的最佳等效 LINQ 表达式,花了我更长的时间:

var invoices = (
    from I in Invoices
    where I.Date >= start &&
          I.Date <= end
    join P in Payments on I.InvoiceID equals P.InvoiceID into payments
    select new
        I.InvoiceID, I.CustomerID, AmountInvoiced = I.Amount, InvoiceDate = I.Date,
        AmountPaid = ((decimal?)payments.Select(P=>P.Amount).Sum()).GetValueOrDefault(),
        AmountDue = I.Amount - ((decimal?)payments.Select(P=>P.Amount).Sum()).GetValueOrDefault()
    
).OrderByDescending(row=>row.AmountDue);

当针对 SQL Server 运行时,这将获得等效的结果集。但是,使用 SQL CE 数据库会改变一些事情。 T-SQL 几乎保持不变。我只需要将ISNULL 更改为COALESCE。但是,使用相同的 LINQ 表达式会导致错误:

解析查询时出错。 [令牌行号= 4,
标记行偏移 = 9,错误标记 = SELECT ]

所以我们看一下生成的SQL代码:

SELECT [t3].[InvoiceID], [t3].[CustomerID], [t3].[Amount] AS [AmountInvoiced], [t3].[Date] AS [InvoiceDate], [t3].[value] AS [AmountPaid], [t3].[value2] AS [AmountDue]
FROM (
    SELECT [t0].[InvoiceID], [t0].[CustomerID], [t0].[Amount], [t0].[Date], COALESCE((
        SELECT SUM([t1].[Amount])
        FROM [Payments] AS [t1]
        WHERE [t0].[InvoiceID] = [t1].[InvoiceID]
        ),0) AS [value], [t0].[Amount] - (COALESCE((
        SELECT SUM([t2].[Amount])
        FROM [Payments] AS [t2]
        WHERE [t0].[InvoiceID] = [t2].[InvoiceID]
        ),0)) AS [value2]
    FROM [Invoices] AS [t0]
    ) AS [t3]
WHERE ([t3].[Date] >= @p0) AND ([t3].[Date] <= @p1)
ORDER BY [t3].[value2] DESC

啊!好的,所以在 SQL Server 上运行时它是丑陋且低效的,但我们不应该关心,因为它应该更快地编写,性能差异应该不会那么大。但它对 SQL CE 不起作用工作,后者显然不支持 SELECT 列表中的子查询。

事实上,我已经在 LINQ 中尝试了几种不同的左连接查询,它们似乎都有同样的问题。甚至:

from I in Invoices
join P in Payments on I.InvoiceID equals P.InvoiceID into payments
select newI, payments

生成:

SELECT [t0].[InvoiceID], [t0].[CustomerID], [t0].[Amount], [t0].[Date], [t1].[InvoiceID] AS [InvoiceID2], [t1].[Amount] AS [Amount2], [t1].[Date] AS [Date2], (
    SELECT COUNT(*)
    FROM [Payments] AS [t2]
    WHERE [t0].[InvoiceID] = [t2].[InvoiceID]
    ) AS [value]
FROM [Invoices] AS [t0]
LEFT OUTER JOIN [Payments] AS [t1] ON [t0].[InvoiceID] = [t1].[InvoiceID]
ORDER BY [t0].[InvoiceID]

这也会导致错误:

解析查询时出错。 [令牌行号= 2,
标记行偏移 = 5,错误标记 = SELECT ]

那么如何使用 LINQ 对 SQL CE 数据库进行简单的左连接?我是在浪费时间吗?

【问题讨论】:

【参考方案1】:

您是否尝试过使用更接近您的 T-SQL 版本的 group by 的查询表达式?

var invoices =
    from I in Invoices
    where I.Date >= start && I.Date <= end
    join P in Payments on I.InvoiceID equals P.InvoiceID into J
    group J.Sum(p => p.Amount) by new  I.InvoiceID, I.CustomerID, I.Amount, I.Date  into G
    let AmountPaid = G.Sum()
    let AmountDue = G.Key.Amount - AmountPaid
    orderby AmountDue descending
    select new
    
        G.Key.InvoiceID,
        G.Key.CustomerID,
        AmountInvoiced = G.Key.Amount,
        InvoiceDate = G.Key.Date,
        AmountPaid,
        AmountDue
    ;

结果与内存中的集合相匹配:

var Invoices = new[] 
    new  InvoiceID = 1, CustomerID = 2, Amount = 2.5m, Date = DateTime.Today ,
    new  InvoiceID = 2, CustomerID = 3, Amount = 5.5m, Date = DateTime.Today 
.AsQueryable();
var Payments = new[] 
    new  InvoiceID = 1, Amount = 1m 
.AsQueryable();

产量:

 InvoiceID = 2, CustomerID = 3, AmountInvoiced = 5.5, InvoiceDate = 8/15/2009,
  AmountPaid = 0, AmountDue = 5.5 
 InvoiceID = 1, CustomerID = 2, AmountInvoiced = 2.5, InvoiceDate = 8/15/2009, 
  AmountPaid = 1, AmountDue = 1.5 

如果这不起作用,LINQ 左连接通常在连接结果上使用DefaultIfEmpty()。您可能不得不这样做:

var invoices =
    from I in Invoices
    where I.Date >= start && I.Date <= end
    join P in Payments on I.InvoiceID equals P.InvoiceID into J
    from PJ in J.DefaultIfEmpty() // Left Join
    group PJ by new  I.InvoiceID, I.CustomerID, I.Amount, I.Date  into G
    let AmountPaid = G.Sum(p => p == null ? 0 : p.Amount)
    // etc...

【讨论】:

没有DefaultIfEmpty() 的第一个解决方案会出现与我的示例类似的错误。你的第二个例子有效!谢谢!我也会在我的其他左连接尝试中尝试这种技术。

以上是关于SQL CE 上的 LINQ 聚合左连接的主要内容,如果未能解决你的问题,请参考以下文章

Linq to Sql:多个左外连接

如何在 LINQ 中使用左外连接进行 SQL 查询?

csharp 左连接LinQ SQL

将 SQL 转换为带有 null 的 Linq 左连接

在 Linq 中使用左外连接

Linq to Sql 左连接查询