优化LINQ查询所需的帮助

时间:2009-07-16 00:21:08

标签: linq subquery junction-table

我希望优化我的LINQ查询,因为尽管它运行正常,但它生成的SQL却是错综复杂且低效的......

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

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表中选择的。 Fyi,DisplayName是一个使用Customer.FirstName和LastName的扩展方法。我有什么想法限制Person的列?

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

感谢任何帮助/建议。

3 个答案:

答案 0 :(得分:1)

第一步是从CustomerProducts(作为Alex Said)开始查询:

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
    };

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

接下来,您应该考虑在Customers和RegisteredCustomers之间创建外键关系。这将导致查询如下所示:

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);

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

正如您所发现的,使用定义为扩展函数或部分类的属性将要求首先对整个对象进行水合,然后在客户端进行选择投影,因为服务器不知道这些额外的属性。很高兴你的代码运行完毕。如果要在查询中的其他位置使用非映射值(除了在投影中),您可能会看到运行时异常。如果您尝试在Where子句中使用Customer.Person.DisplayName属性,则可以看到此内容。正如您所发现的,修复是直接在投影子句中进行字符串连接。

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

相关问题