我正在尝试编写一个Linq to SQL语句,该语句显示所有客户记录,并且只显示发票表的匹配最大值(InvoiceId);基本上是客户的最新发票。左连接是必需的,因为客户可能没有任何发票,但需要在结果集中。
两个基本表,其外键为Customer.CustomerID = Invoice.CustomerId
CREATE TABLE [dbo].[Customer](
[CusomerId] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [int] NOT NULL
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
(
[CustomerId] ASC
)
) ON [PRIMARY]
CREATE TABLE [dbo].[Invoice](
[InvoiceId] [int] IDENTITY(1,1) NOT NULL,
[CustomerId] [int] NOT NULL,
[InvoiceTotal] [float] NOT NULL
CONSTRAINT [PK_Invoice] PRIMARY KEY CLUSTERED
(
[InvoiceId] ASC
)
) ON [PRIMARY]
所需结果集的SQL如下:
SELECT *
FROM Customers c
LEFT JOIN
(Invoice i
INNER JOIN (SELECT CustomerId, MAX(InvoiceId) as InvId FROM Invoice GROUP BY CustomerId) as InvList
ON i.InvoiceNo = InvList.InvoiceNo) ON c.CustomerId = i.CustomerId
根据我的发现,我不认为这可以在一个声明中完成;需要首先创建MAX(InvoiceId)产品并在主语句中使用。由于我不能让它工作,也许我也错了。
答案 0 :(得分:3)
您可以在LINQ中编写此特定查询,如下所示 - 尽管这会产生相关的子查询:
var query =
from c in ctx.Customer
select new
{
Customer = c,
LatestInvoice = ctx.Invoice
.Where(i => i.CustomerId == c.CustomerId)
.OrderByDescending(i => i.InvoiceId)
.FirstOrDefault();
};
如果您想以另一种方式执行此操作,LINQ语法的可读性较差,但由于延迟执行,可以将查询拆分一点:
var latestInvoicesPerCustomerQuery =
from inv in ctx.Invoice
group inv by inv.CustomerId into g
select new { CustomerId = g.Key, InvoiceId = g.Max(inv => inv.InvoiceId) };
var customersAndLatestInvoicesQuery =
from customer in ctx.Customer
join linv in latestInvoicesPerCustomer
on customer.CustomerId equals linv.CustomerId
into latestInvoiceJoin
from latestInvoice in latestInvoiceJoin.DefaultIfEmpty() // left join
join invoice in ctx.Invoice
on latestInvoice.InvoiceId equals invoice.InvoiceId
select new
{
Customer = customer,
LatestInvoice = invoice
};
第一个查询(latestInvoicesPerCustomerQuery
)在您枚举它或第二个查询引用第一个查询之前不会执行。就运行时而言,最终查询是一个表达式树 - 因此您可以将第一个查询视为已吸收到第二个查询中。
如果你真的想在一个查询中使用它,你也可以这样做:
var customersAndLatestInvoicesQuery =
from customer in ctx.Customer
join linv in (
from inv in ctx.Invoice
group inv by inv.CustomerId into g
select new
{
CustomerId = g.Key,
InvoiceId = g.Max(inv => inv.InvoiceId)
}
)
on customer.CustomerId equals linv.CustomerId
into latestInvoiceJoin
from latestInvoice in latestInvoiceJoin.DefaultIfEmpty() // left join
join invoice in ctx.Invoice
on latestInvoice.InvoiceId equals invoice.InvoiceId
select new
{
Customer = customer,
LatestInvoice = invoice
};
customersAndLatestInvoicesQuery
的任何一种变体都应该大致转换为您在帖子中列出的SQL。
答案 1 :(得分:0)
我无法让Ben M的例子工作,但我能够将其用于以下方面:
var latestInvoicesPerCustomerQuery =
from inv in ctx.Invoice
group inv by inv.CustomerId into g
join invj in ctx.Invoice on g.Max(inv => inv.InvoiceId) equals invj.InvoiceId
select invj;
var customersAndLatestInvoicesQuery =
from customer in ctx.Customer
join linv in latestInvoicesPerCustomer
on customer.CustomerId equals linv.CustomerId
into latestInvoiceJoin
from latestInvoice in latestInvoiceJoin.DefaultIfEmpty() // left join
select new
{
Customer = customer,
LatestInvoice = invoice
};
在第一个声明中,我将发票表加入了结果,故意不使用select new。
//If I had done:
select new {LatestInvoice=invj}
// then I would have included the name LatestInvoice in the second statement:
join linv in latestInvoicesPerCustomer
on customer.CustomerId equals linv.LatestInvoice.CustomerId
into latestInvoiceJoin
// Not desirable to me, and it seems it may be troublesome when used.
这实际上简化了第二个语句,只需要对第一个语句对象进行简单的左连接。我现在正在为拥有这些客户的客户提供所有客户和最新发票的结果集。
我不确定为什么Ben M的解决方案不起作用,但是当删除以下行时,我只会得到一个左连接产品:
join invoice in ctx.Invoice
on latestInvoice.InvoiceId equals invoice.InvoiceId
包含此行,产品是内部联接。