优化 LINQ 查询所需的帮助

发布于 2024-07-27 13:39:43 字数 1060 浏览 2 评论 0原文

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

基本上,我希望选择订购所需产品 (reqdProdId) 并注册的客户(作为 CustomerDisplay 对象)带有信用卡号(以行的形式存储在 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 表中选择了所有列。 仅供参考,DisplayName 是一种使用 Customer.FirstName 和 LastName 的扩展方法。 有什么想法可以限制 Person 的列吗?

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

任何帮助/建议表示赞赏。

I am looking to optimize my LINQ query because although it works right, the SQL it generates is convoluted and inefficient...

Basically, I am looking to select customers (as CustomerDisplay objects) who ordered the required product (reqdProdId), and are registered with a credit card number (stored as a row in RegisteredCustomer table with a foreign key 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
            };

As an overview, a Customer has a corresponding Person which has the Name; PersonID is a foreign key in Customer table.
If I look at the SQL generated, I see all columns being selected from the Person table. Fyi, DisplayName is an extension method which uses Customer.FirstName and LastName. Any ideas how I can limit the columns from Person?

Secondly, I want to get rid of the Any clause (and use a sub-query) to select all other CustomerIds who have the required ProductID, because it (understandably) generates an Exists clause.
As you may know, LINQ has a known issue with junction tables, so I cannot just do a cust.CustomerProducts.Products.
How can I select all Customers in the junction table with the required ProductID?

Any help/advice is appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浅紫色的梦幻 2024-08-03 13:39:43

第一步是从 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
    };

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

接下来,您应该考虑在 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);

The first step is to start your query from CustomerProducts (as 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
    };

This will simplify your syntax and hopefully result in a better execution plan.

Next, you should consider creating a foreign key relationship between Customers and RegisteredCustomers. This would result in a query that looked like this:

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

Finally, for optimum speed, have LINQ compile your query at compile time, rather than run time by using a compiled query:

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

You can call the compiled query like this:

IQueryable<CustomerDisplay> myCustDisplay = GetCustWithProd(db, myParams);
夢归不見 2024-08-03 13:39:43

我建议从有问题的产品开始查询,例如:

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

I'd suggest starting your query from the product in question, e.g. something like:

from cp in db.CustomerProducts
join .....
where cp.ProductID == reqdProdID
半仙 2024-08-03 13:39:43

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

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

As you have found, using a property defined as an extension function or in a partial class will require that the entire object is hydrated first and then the select projection is done on the client side because the server has no knowledge of these additional properties. Be glad that your code ran at all. If you were to use the non-mapped value elsewhere in your query (other than in the projection), you would likely see a run-time exception. You can see this if you try to use the Customer.Person.DisplayName property in a Where clause. As you have found, the fix is to do the string concatenation in the projection clause directly.

Lame Duck, I think there is a bug in your code as the cust variable used in your select clause isn't declared elsewhere as a source local variable (in the from clauses).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文