1

LINQ クエリは正しく機能しますが、生成される SQL は複雑で非効率的であるため、LINQ クエリを最適化しようとしています...

基本的に、必要な製品 (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
            };

概要として、Customer には Name を持つ対応する Person があります。PersonID は Customer テーブルの外部キーです。生成された SQL を見ると、Person テーブルからすべての列が選択されていることがわかります。参考までに、DisplayName は Customer.FirstName と LastName を使用する拡張メソッドです。Person の列を制限する方法はありますか?

2 番目に、必要な ProductID を持つ他のすべての CustomerIds を選択するために、Any 句を削除 (およびサブクエリを使用) したいと考えています。これは、(当然ながら) Exists 句を生成するためです。ご存じかもしれませんが、LINQ にはジャンクション テーブルに関する既知の問題があるため、cust.CustomerProducts.Products だけを実行することはできません。必要な ProductID を持つジャンクション テーブル内のすべての顧客を選択するにはどうすればよいですか?

ヘルプ/アドバイスをいただければ幸いです。

4

3 に答える 3

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

これにより、構文が簡素化され、うまくいけば実行計画が改善されます。

次に、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);
于 2009-07-16T00:41:21.080 に答える
0

おわかりのように、拡張関数として定義されたプロパティまたは部分クラスで定義されたプロパティを使用するには、最初にオブジェクト全体をハイドレートしてから、クライアント側で選択プロジェクションを実行する必要があります。これは、サーバーがこれらの追加プロパティを認識していないためです。あなたのコードがまったく実行されたことをうれしく思います。マップされていない値をクエリの他の場所 (プロジェクション以外) で使用すると、実行時例外が発生する可能性があります。これは、Where 句で Customer.Person.DisplayName プロパティを使用しようとすると確認できます。あなたが見つけたように、修正は射影句で文字列連結を直接行うことです。

不自由なダックさん、select 句で使用されている cust 変数がソース ローカル変数として (from 句で) 宣言されていないため、コードにバグがあると思います。

于 2009-07-30T22:33:54.833 に答える
0

問題の製品からクエリを開始することをお勧めします。たとえば、次のようになります。

from cp in db.CustomerProducts
join .....
where cp.ProductID == reqdProdID
于 2009-07-16T00:34:18.910 に答える