我需要的是这样一个简单易用的查询,它让我感到震惊的是我在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表达式会导致错误:
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数据库上进行简单的左连接?我在浪费时间吗?
答案 0 :(得分: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...