如何更好地编写这个LINQ查询

3

我有一个Linq查询。当我运行这个查询时,仅提取10条记录的数据到模型中需要13秒钟的时间。我需要知道我编写的查询是否对性能有好处。请指导我做错了什么。

代码

    var stocktakelist = (from a in Db.Stocktakes
                             select new ExportStock
                                 {
                                     Id = a.Id,
                                     ItemNo = a.ItemNo,
                                     AdminId = (from admin in Db.AdminAccounts where admin.Id == a.Id select admin.Name).FirstOrDefault(),
                                     CreatedOn = a.CreatedOn,
                                     Status = (from items in Db.Items where items.ItemNo == a.ItemNo select items.ItemStatu.Description).FirstOrDefault(),
                                     Title = (from tit in Db.BibContents where tit.BibId == (from bibs in Db.Items where bibs.ItemNo == a.ItemNo select bibs.BibId).FirstOrDefault() && tit.TagNo == "245" && tit.Sfld == "a" select tit.Value).FirstOrDefault()   // This line of Query only makes the performance Issue
                                 }
            ).ToList();

谢谢


2
我认为你需要一些JOIN操作。看一下这里和这里。另外,请确保DB.Stocktakes是IQueryable类型的。我发现IQueryable比IEnumerable更快,因为IQueryable在数据库中执行大部分繁重的工作,而不是先检索所有数据,然后再进行一些技巧性的处理。 - jao
2
只是来这里说“tit” :) 正如jao所说,如果对象中没有导航属性,则连接是正确的方法。此外,请确保数据库中有Items.ItemNo等索引。 - Alex
你可以先编写一个 SQL 查询来获取所需的数据,然后将其转录为 LINQ 查询,这可能会对你有所帮助。当我在编写 LINQ 查询时遇到困难时,我会采用这种方法。 - Thelonias
一个方便的提示是尝试在LINQPad中运行您的代码,因为结果窗格中有一个SQL选项卡,可以显示从Linq语句生成的确切SQL。通过这个,您可能能够推断出更好的编写表达式的方法。 - Peter Monks
4个回答

2
这个操作很慢的原因是它在外部LINQ语句中的每个项上运行3个内部LINQ语句。使用LINQ连接将仅运行4个查询,然后将它们链接在一起,这样更快。
要了解如何连接,请根据您使用的LINQ类型在互联网上查找资源。
如果您从SQL服务器检索此数据,则可以考虑在SQL中执行此项密集工作-这就是SQL设计的目的,并且比.NET快得多。编辑:如下所示,如果使用LINQ到SQL /实体并使用正确的连接语法,则会在SQL中完成工作。

1
如果操作者正确使用连接,那么“密集工作”将在SQL中完成。 - Rawling
假设 OP 正在使用 LINQ to SQL/Entities(或其他?)。然而,仅使用原始的 LINQ 并不能自动从 SQL 中获取数据。 - jaypeagi

1

正如其他人所说,您应该在LINQ中使用左外连接,就像在SQL中编写一样。

一旦转换,您上面的查询大致会看起来像这样(尚未经过测试,但提供了基本思路):

var a = from a in Db.Stocktakes
    join admin in Db.AdminAccounts on admin.Id equals a.Id into tmpAdmin
    from ad in tmpAdmin.DefaultIfEmpty()
    join item in Db.Items on item.ItemNo equals a.ItemNo into tmpItem

    from it in tmpItem.DefaultIfEmpty()
    join title in Db.BibContents on bib.BibId equals items.BibId into tmpTitle

    from ti in tmpTitle.DefaultIfEmpty()
    where ti.TagNo == "245" 
        && ti.Sfld == "a"
    select new ExportStock
    {
        Id = a.Id,
        ItemNo = a.ItemNo,
        AdminId = ad == null ? default(int?) : ad.Id,
        CreatedOn = a.CreatedOn,
        Status = it == null ? default(string) : it.ItemStatus.Description,
        Title = ti == null ? default(string) : ti.Value
    };

1

我试图创建一些连接练习的相应查询。 我无法测试它,也不确定这个查询是否能够得到你所希望的结果, 但至少它可以给你一个关于如何使用linq编写连接的提示。

from a in Db.Stocktakes

join admin in Db.AdminAccounts
  on a.Id equals admin.Id
into adminJoinData
from adminJoinRecord in adminJoinData.DefaultIfEmpty( )

join items in Db.Items
  on a.ItemNo equals items.ItemNo
into itemsJoinData
from itemsJoinRecord in itemsJoinData.DefaultIfEmpty( )

join title in Db.BibContents
   (
       from subQuery in Db.BibContents
       where subQuery.TagNo == "245"
       where subQuery.Sfld == "a"
       select subquery
   )
  on title.BibId equals itemsJoinRecord.BidId
into titleJoinData
from titleJoinRecord in titleJoinData.DefaultIfEmpty( )

select new ExportStock( )
{
    Id = a.Id,
    ItemNo = a.ItemNo,
    AdminId = adminJoinRecord.Name,
    CreatedOn = a.CreatedOn,
    Status = itemsJoinRecord.ImemStatu.Description,
    Title = titleJoinRecord.Value
}

我尝试了你的编码方式。它运行良好。感谢您的回复。 - Dheyv

0

使用 Lambda 表达式,您的查询将如下所示:

        Db.Stocktakes
            .Join(Db.AdminAccounts, a => a.Id, b => b.Id, (a,b) => new { a, AdminId = b.Name })
            .Join(Db.Items, a => a.ItemNo, b => b.ItemNo, (a,b) => new { a, Status = b.ItemStatus.Description, BidId = b.BibId })
            .Join(Db.BibContents, a => a.BibId, b => b.BibId, (a,b) => new { a, Value = b.Value, TagNo = b.TagNo, Sfld = b.Sfld })
            .Where(a => a.TagNo == "245" && a.Sfld == "a")
            .Select(a => 
                new ExportStock { Id = a.Id, 
                                  ItemNo = a.ItemNo,
                                  AdminId = a.AdminId,
                                  CreatedOn = a.CreatedOn,
                                  Status = a.Status,
                                  Title = a.Value
                }
            ).ToList();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接