【问题标题】:SubQuery and Group by in linqlinq 中的子查询和分组依据
【发布时间】:2013-06-04 11:37:52
【问题描述】:

这个查询是不久前在我们的系统中编写的,但是随着数据的一点点增加,这个查询的性能变得很差。我的调查显示 (CodeCount) 查询触发另一个子查询导致执行的大量延迟。我需要优化这个 Linq 查询。任何帮助将不胜感激

  from batch in Context.VoucherCodeBatch.ToList()
                    join type in Context.VoucherCodeType on batch.VoucherTypeId equals type.VoucherTypeId
                    join voucher in Context.Voucher on batch.VoucherCodeBatchId equals voucher.VoucherCodeBatchId

                    where batchIds.Contains(batch.BatchCode)
                    group new
                    {
                        batch.BatchCode,
                        batch.CreationDate,
                        type.VoucherTypeName,
                        voucher.AllowedCount,
                        voucher.ValidFrom,
                        voucher.ValidTo,
                        batch.VoucherCodeBatchId,
                        voucher.VoucherCode
                    }
                        by new { batch.BatchCode }
                        into uniquebatch
                        select new Batch
                        {
                            BatchCode = uniquebatch.FirstOrDefault().BatchCode,
                            CreationDate = uniquebatch.FirstOrDefault().CreationDate,
                            TimesAllowed = uniquebatch.FirstOrDefault().AllowedCount,
                            ValidFrom = uniquebatch.FirstOrDefault().ValidFrom,
                            CodeCount = ((from c in Context.Voucher.ToList()
                                          where
                                              c.VoucherCodeBatchId ==
                                              uniquebatch.FirstOrDefault().VoucherCodeBatchId
                                          select c).Count()),
                            ValidTo = uniquebatch.FirstOrDefault().ValidTo,
                            CodeType = uniquebatch.FirstOrDefault().VoucherTypeName,
                            VoucherCodeBatchId = uniquebatch.FirstOrDefault().VoucherCodeBatchId
                        });

【问题讨论】:

  • 其中的ToList() 表示此查询正在从数据库中请求所有 行凭证并在内存中进行过滤。尝试删除它。
  • 我建议在数据库端的 SQL 中执行此类查询。
  • 我同意埃瓦尔德的观点。甚至可以将其制成一个视图,然后您可以在 LINQ 中更轻松地选择它。很多时候,仅仅因为你可以在 LINQ 中做到这一点并不意味着它是最好的方法。

标签: c# asp.net .net linq linq-to-sql


【解决方案1】:

第一个大问题是 ObjectSet(EF 中的表集合)前面的 ToList()。

永远不要这样做,ToList() 会强制 EF 在处理查询之前将所有数据带入内存。 (喜欢@Daniel Hilgarth 的评论)。

其他细节是在FirstOrDefault() 前面使用get 属性,如行中:

BatchCode = uniquebatch.FirstOrDefault().BatchCode,

在这种情况下使用First() 代替FirstOrDefault。喜欢:

BatchCode = uniquebatch.First().BatchCode,

您的查询将是这样的:

from batch in Context.VoucherCodeBatch/*.ToList()*/
join type in Context.VoucherCodeType on batch.VoucherTypeId equals type.VoucherTypeId
join voucher in Context.Voucher on batch.VoucherCodeBatchId equals voucher.VoucherCodeBatchId
where batchIds.Contains(batch.BatchCode)
group new
    {
        batch.BatchCode,
        batch.CreationDate,
        type.VoucherTypeName,
        voucher.AllowedCount,
        voucher.ValidFrom,
        voucher.ValidTo,
        batch.VoucherCodeBatchId,
        voucher.VoucherCode
    }
by new { batch.BatchCode }
into uniquebatch
select ( delegate
    {
        // If you put a operation in a query that operation will be
        // processed all times. Bacause that i removed this line from
        // the where statement.
        var vcBatchId = uniquebatch.First().VoucherCodeBatchId;

        return new Batch
            {
                BatchCode = uniquebatch.First().BatchCode,
                CreationDate = uniquebatch.First().CreationDate,
                TimesAllowed = uniquebatch.First().AllowedCount,
                ValidFrom = uniquebatch.First().ValidFrom,
                CodeCount = ((
                    from c in Context.Voucher/*.ToList()*/
                    where c.VoucherCodeBatchId == vcBatchId
                    select c).Count()),
                ValidTo = uniquebatch.First().ValidTo,
                CodeType = uniquebatch.First().VoucherTypeName,
                VoucherCodeBatchId = uniquebatch.First().VoucherCodeBatchId
            }
    });

如果这个改进不够好,您需要将此查询更改为 SQL。但我相信这种改进会表现得更好。

【讨论】:

  • 感谢您对此进行调查。代表是什么意思。我完全复制了上面的代码,编译器抱怨 select(delegate) “select 子句中表达式的类型不正确。调用‘Select’时类型推断失败”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多