优化 LINQ 查询所需的帮助

Posted

技术标签:

【中文标题】优化 LINQ 查询所需的帮助【英文标题】:Help required to optimize LINQ query 【发布时间】:2009-07-16 00:21:07 【问题描述】:

我希望优化我的 LINQ 查询,因为虽然它可以正常工作,但它生成的 SQL 复杂且效率低下...

基本上,我希望选择订购所需产品 (reqdProdId) 并使用信用卡号注册的客户(作为 CustomerDisplay 对象)(使用外键 CustId 存储为 RegisteredCustomer 表中的一行)

var q = from cust in db.Customers
        join regCust in db.RegisteredCustomers on cust.ID equals regCust.CustId
        where cust.CustomerProducts.Any(co => co.ProductID == reqdProdId)
        where regCust.CreditCardNumber != null && regCust.Authorized == true  
        select new  CustomerDisplay
            
              Id = cust.Id,
              Name = cust.Person.DisplayName,
              RegNumber = cust.RegNumber
            ;

总而言之,客户有一个对应的人,该人具有名称; PersonID 是 Customer 表中的外键。 如果查看生成的 SQL,我会看到从 Person 表中选择了所有列。仅供参考,DisplayName 是使用 Customer.FirstName 和 LastName 的扩展方法。有什么想法可以限制来自 Person 的列吗?

其次,我想摆脱 Any 子句(并使用子查询)来选择具有所需 ProductID 的所有其他 CustomerId,因为它(可以理解)生成一个 Exists 子句。 您可能知道,LINQ 有一个与联结表有关的已知问题,所以我不能只做一个 cust.CustomerProducts.Products。 如何选择联结表中具有所需 ProductID 的所有客户?

感谢任何帮助/建议。

【问题讨论】:

【参考方案1】:

第一步是从 CustomerProducts 开始您的查询(正如 Alex 所说):

IQueryable<CustomerDisplay> myCustDisplay =
    from custProd in db.CustomerProducts
    join regCust in db.RegisteredCustomers 
        on custProd.Customer.ID equals regCust.CustId
    where
        custProd.ProductID == reqProdId
        && regCust.CreditCardNumber != null
        && regCust.Authorized == true
    select new CustomerDisplay
    
      Id = cust.Id,
      Name = cust.Person.Name,
      RegNumber = cust.RegNumber
    ;

这将简化您的语法,并有望产生更好的执行计划。

接下来,您应该考虑在客户和注册客户之间创建外键关系。这将产生如下所示的查询:

IQueryable<CustomerDisplay> myCustDisplay =
    from custProd in db.CustomerProducts
    where
        custProd.ProductID == reqProdId
        && custProd.Customer.RegisteredCustomer.CreditCardNumber != null
        && custProd.Customer.RegisteredCustomer.Authorized == true
    select new CustomerDisplay
    
      Id = cust.Id,
      Name = cust.Person.Name,
      RegNumber = cust.RegNumber
    ;

最后,为了获得最佳速度,让 LINQ 在编译时编译您的查询,而不是在运行时使用已编译的查询:

Func<MyDataContext, SearchParameters, IQueryable<CustomerDisplay>> 
    GetCustWithProd =
    System.Data.Linq.CompiledQuery.Compile(
        (MyDataContext db, SearchParameters myParams) =>
        from custProd in db.CustomerProducts
        where
            custProd.ProductID == myParams.reqProdId
            && custProd.Customer.RegisteredCustomer.CreditCardNumber != null
            && custProd.Customer.RegisteredCustomer.Authorized == true
        select new CustomerDisplay
        
          Id = cust.Id,
          Name = cust.Person.Name,
          RegNumber = cust.RegNumber
        ;
    );

你可以这样调用编译后的查询:

IQueryable<CustomerDisplay> myCustDisplay = GetCustWithProd(db, myParams);

【讨论】:

thansk Lame Duck,从来没有想过编译的 LINQ ......会研究一下。这是一些很好的函数式编程的东西。另外,我在 Customer 和 RegisteredCustomer 之间确实有一个外键,我不确定 custProd.Customer.RegisteredCustomer.CreditCardNumber != null (正如你所建议的那样)和 custProd.CreditCardNumber != null (正如我所拥有的) LINQ 计算出 custProd 是联接中的行,生成的 SQL 是 INNER JOIN RegisteredCustomer as t2 on t1.Id = t2.CustomerID ... ... t2.CreditCardNumber IS NOT NULL 重新编译查询:查询仍然在运行时编译,而不是在编译时。这仅在您多次使用同一个查询时才有用,因为您可以缓存已编译的查询并重用它。【参考方案2】:

我建议您从相关产品开始查询,例如类似:

from cp in db.CustomerProducts
join .....
where cp.ProductID == reqdProdID

【讨论】:

非常感谢亚历克斯。我翻转了LINQ,它处理了连接表的东西。有人对扩展方法问题有任何见解吗? 我假设如果您的代码仅访问 Person.FirstName 和 Person.LastName,那么这就是所有将被选中的内容。远射:您是否尝试过不使用扩展方法?例如姓名 = cust.Person.FirstName + " " + cust.Person.LastName 我同意,我在LINQPad中测试了一下,发现如果直接指定两个字段ala DisplayName = cust.FirstName + " " + cust.LastName,生成的SQL只有这两个字段【参考方案3】:

正如您所发现的,使用定义为扩展函数或在部分类中的属性将需要首先对整个对象进行水合,然后在客户端完成选择投影,因为服务器不知道这些附加特性。很高兴您的代码完全运行。如果您要在查询的其他地方(投影之外)使用非映射值,您可能会看到运行时异常。如果您尝试在 Where 子句中使用 Customer.Person.DisplayName 属性,您会看到这一点。如您所见,解决方法是直接在投影子句中进行字符串连接。

Lame Duck,我认为您的代码中存在错误,因为您的 select 子句中使用的 cust 变量未在其他地方声明为源局部变量(在 from 子句中)。

【讨论】:

以上是关于优化 LINQ 查询所需的帮助的主要内容,如果未能解决你的问题,请参考以下文章

优化 PostgreSql 查询以获取找到的记录总数和基于多个 group by 的分页所需的有限行数

如何编写/优化需要从 6 个相关表中选择数据的 sql 查询,直到获得所需的数据

使用 LINQ to SQL 类所需的帮助

如何使用一个 linq 查询获得总数量?

如何查询/将数据转换为所需的格式

ms 访问查询仅检索所需的值