Требуется помощь для оптимизации запроса LINQ - PullRequest
1 голос
/ 16 июля 2009

Я стремлюсь оптимизировать мой запрос 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. Кстати, DisplayName - это метод расширения, который использует Customer.FirstName и LastName. Любые идеи, как я могу ограничить столбцы от человека?

Во-вторых, я хочу избавиться от предложения Any (и использовать подзапрос), чтобы выбрать все другие CustomerIds, которые имеют требуемый ProductID, потому что он (понятно) генерирует предложение Exists. Как вы, возможно, знаете, у LINQ есть известная проблема с соединительными таблицами, поэтому я не могу просто сделать cust.CustomerProducts.Products. Как выбрать всех клиентов в соединительной таблице с требуемым ProductID?

Любая помощь / совет приветствуется.

Ответы [ 3 ]

1 голос
/ 16 июля 2009

Первый шаг - начать запрос с CustomerProducts (как сказал Алекс):

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);
0 голосов
/ 31 июля 2009

Как вы обнаружили, использование свойства, определенного как функция расширения или в частичном классе, потребует сначала гидратации всего объекта, а затем выбор проекции выполняется на стороне клиента, поскольку сервер не знает об этих дополнительных свойства. Радуйтесь, что ваш код работает вообще. Если бы вы использовали не сопоставленное значение в другом месте вашего запроса (кроме проекции), вы, скорее всего, увидели бы исключение во время выполнения. Это можно увидеть, если вы попытаетесь использовать свойство Customer.Person.DisplayName в предложении Where. Как вы обнаружили, исправление заключается в непосредственном объединении строк в предложении проекции.

Lame Duck, я думаю, что в вашем коде есть ошибка, поскольку переменная cust, используемая в вашем предложении select, нигде не объявлена ​​как локальная переменная источника (в предложениях from).

0 голосов
/ 16 июля 2009

Я бы посоветовал начать ваш запрос с рассматриваемого продукта, например, что-то вроде:

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