2009-08-15 111 views
1

我需要的是这样一个简单的,简单的查询,它让我感到我在LINQ中尝试做了多少工作。在T-SQL,这将是:LINQ聚合SQL CE上的左连接

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表达,但是,导致一个错误:

There was an error parsing the query. [ Token line number = 4, 
Token line offset = 9,Token in error = 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 new{I, 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] 

这也导致错误:

There was an error parsing the query. [ Token line number = 2, 
Token line offset = 5,Token in error = SELECT ]

那么,如何可以做一个简单的左连接使用LINQ一个SQL CE数据库?我在浪费时间吗?

回答

3

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

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... 
+0

你的第一个解决方案没有'DefaultIfEmpty()'爆炸与我的例子类似的错误。你的第二个例子有效!谢谢!我会在我的其他左连接尝试中尝试这种技巧。 – 2009-08-15 21:40:35