优化LINQ查询
本文关键字:查询 LINQ 优化 | 更新日期: 2023-09-27 18:05:21
我有一个报告,显示了向一个确定的商家发出的订单,并且它工作得很好,直到我需要为支付状态添加一个过滤器。
这就是我如何构建查询,逐个过滤器:
var queryOrder = context.Orders.Select(m=>m);
if (viewModel.InitialDate.HasValue)
queryOrder = queryOrder.Where(m => m.CreatedDate.Date >= viewModel.InitialDate.Value);
(...) /* continues building the query, filter by filter */
if (viewModel.SelectedPaymentStatus != null)
queryOrder = queryOrder.Where(m => viewModel.SelectedPaymentStatus.Contains(m.Payments.Select(p => p.PaymentStatusId).Single().ToString()));
queryOrder = queryOrder.Where(m => m.MerchantId == merchantId);
当我运行queryOrder
时,即使它只是一个queryOrder.Count()
,它也需要超过1分钟的时间来执行。使用SQL Server的分析工具,我提取生成的查询如下:
SELECT [t0].[Id], [t0].[CustomerId], [t0].[MerchantId], [t0].[OrderNumber], [t0].[Amount], [t0].[SoftDescriptor], [t0].[ShippingMethod], [t0].[ShippingPrice], [t0].[IpAddress], [t0].[SellerComment], [t0].[CreatedDate]
FROM [dbo].[Order] AS [t0]
WHERE ([t0].[MerchantId] = @p0)
AND ((CONVERT(NVarChar,(
SELECT [t1].[PaymentStatusId]
FROM [dbo].[Payment] AS [t1]
WHERE [t1].[OrderId] = [t0].[Id]
))) IN (@p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8))
@p0参数是merchantId的Guid, @p1到@p8是数字字符串"1"
到"8"
,代表paymentStatusId的。
如果我跳过这行:
if (viewModel.SelectedPaymentStatus != null)
queryOrder = queryOrder.Where(m => viewModel.SelectedPaymentStatus.Contains(m.Payments.Select(p => p.PaymentStatusId).Single().ToString()));
查询运行时间不到1秒。但是当我使用它的时候,它的表现却很糟糕。关于如何解决这个问题有什么建议吗?
所有的查询都被延迟,这是linq的好/坏的部分。尝试拆分查询并使用一些内存中的结果。尝试删除第一个查询(实际上没有多大意义,您返回的是相同的集合),并将第二个查询修改为这样,看看它是否有任何不同。
var clause = context.Orders.Payments.Select(p => p.PaymentStatusId).Single().ToString();
if (viewModel.SelectedPaymentStatus != null)
var queryOrder = context.Orders.queryOrder.Where(m => viewModel.SelectedPaymentStatus.Contains(clause));
我已经注释了问题代码片段:
if (viewModel.SelectedPaymentStatus != null) {
// Give me only orders from queryOrder where...
queryOrder = queryOrder.Where(
// ...my viewModel's SelectedPaymentStatus collection...
m => viewModel.SelectedPaymentStatus.Contains(
// ...contains the order's payment's PaymentStatusId...
m.Payments.Select(p => p.PaymentStatusId).Single()
// ... represented as a string?!
.ToString()
// Why are database IDs strings?
)
);
}
viewModel.SelectedPaymentStatus
似乎是一个字符串集合;因此,您要求数据库将PaymentStatusId
转换为nvarchar,并与SelectedPaymentStatus
的元素进行字符串比较。恶心。
由于viewModel.SelectedPaymentStatus
很小,最好创建一个临时的List<int>
并在查询中使用:
if (viewModel.SelectedPaymentStatus != null) {
// Let's do the conversion once, in C#
List<int> statusIds = viewModel.SelectedPaymentStatus.Select( i => Convert.ToInt32(i) ).ToList();
// Now select the matching orders
queryOrder = queryOrder.Where(
m => statusIds.Contains(
m.Payments.Select(p => p.PaymentStatusId).Single())
)
);
}