2009-07-16 53 views
1

我期待,因爲雖然它的作品的權利,它生成的SQL是令人費解的和低效的優化我的LINQ查詢...有助於優化LINQ查詢

基本上,我期待客戶選擇(如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的擴展方法。任何想法如何限制人員列?其次,我想擺脫Any子句(並使用子查詢)來選擇所有具有所需ProductID的CustomerIds,因爲它(可以理解)生成一個Exists子句。你可能知道,LINQ有一個已知的聯結表問題,所以我不能只做一個cust.CustomerProducts.Products。 如何使用所需的ProductID選擇聯結表中的所有客戶?

任何幫助/建議表示讚賞。

回答

1

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

這將簡化你的語法,並希望得到更好的執行計劃。

接下來,您應該考慮在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); 
+0

thansk Lame Duck,從來沒有想過編譯LINQ ...會研究它。這是一些很好的功能性編程的東西。 另外,我確實有客戶和RegisteredCustomer之間的外鍵,我不知道有什麼不同 custProd.Customer.RegisteredCustomer.CreditCardNumber!= NULL之間 (你的建議) 和 custProd.CreditCardNumber! = NULL(像我) LINQ數字出custProd是在加入該行,生成的SQL是 INNER JOIN RegisteredCustomer作爲t1.Id = t2.CustomerID ... ... T2 T2 .CreditCardNumber IS NOT NULL – 2009-07-16 01:00:29

+0

重新編譯查詢:查詢仍在運行時編譯,而不是在編譯時編譯。如果您多次使用相同的查詢,這隻會有所幫助,因爲您可以緩存已編譯的查詢並重新使用它。 – Lucas 2009-08-27 17:24:11

0

我建議您從相關產品開始查詢,例如是這樣的:

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

非常感謝Alex。 我翻轉了LINQ,它照顧了交界處的東西。 任何人對擴展方法問題有任何見解? – 2009-07-16 00:49:37

+0

我假設如果你的代碼只訪問Person.FirstName和Person.LastName,那就是所有的選擇。遠射:你是否嘗試沒有擴展方法?例如Name = cust.Person.FirstName +「」+ cust.Person.LastName – 2009-07-16 01:03:52

+0

我同意,我在LINQPad中測試了它,發現如果直接指定了兩個字段ala DisplayName = cust.FirstName +「」+ cust.LastName,生成的SQL只有這兩個字段 – 2009-07-16 01:04:52

0

正如你已經發現,使用被定義爲一個擴展功能或部分類的屬性將要求整個對象第一水合然後選擇投影在客戶端完成,因爲服務器不知道這些附加屬性。很高興你的代碼可以運行。如果您要在查詢中的其他地方使用非映射值(投影中除外),則可能會看到運行時異常。如果您嘗試在Where子句中使用Customer.Person.DisplayName屬性,則可以看到此內容。正如你所發現的,修正是直接在投影子句中進行字符串連接。

跛腳鴨,我認爲這是在你的代碼在你的SELECT子句中使用的卡斯特變量不是別處源局部變量聲明中的錯誤(在FROM子句)。