我正在處理一個委託跟蹤系統。業務案例需要銷售人員和客戶之間的多對多關係。 (簡化)實體是:實體框架多對多查詢生成粗糙SQL
public class Customer {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<CustomerSeller> CustomerSellers { get; set; }
public virtual ICollection<Payment> Payments { get; set; }
}
public class Seller {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<CustomerSeller> CustomerSellers { get; set; }
}
// join table w/payload for many-to-many relationship
public class CustomerSeller {
public int Id { get; set; }
public Seller Seller { get; set; }
public Customer Customer { get; set; }
public decimal CommissionRate { get; set; }
}
public class Payment {
public int Id { get; set; }
public Customer ReceivedFrom { get; set; }
public DateTime Date { get; set; }
public decimal Amount { get; set; }
}
我一時目標是讓客戶的所有付款鏈接到特定銷售人員的名單。如果我寫直接的SQL它會是這個樣子:
select Payment.*
from Payment
inner join CustomerSeller on CustomerSeller.Customer_Id = Payment.ReceivedFrom_Id
where CustomerSeller.Seller_Id = @sellerIdToQuery
我使用LINQ/EF想這對我的ASP NET MVC的網站使用此代碼:
public ActionResult Sales(int id) {
var qry = (
from p in db.Payments
join cs in db.CustomerSellers on p.ReceivedFrom equals cs.Customer
where cs.Seller.Id == id
select p);
var paymentList = qry.ToList();
return View(paymentList);
}
這並不工作,但幕後的SQL看起來waaay複雜(這是出乎我的SQL解碼能力來分析):
{SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Date] AS [Date],
[Extent1].[Amount] AS [Amount],
[Extent1].[ReceivedFrom_Id] AS [ReceivedFrom_Id]
FROM [dbo].[Payment] AS [Extent1]
INNER JOIN [dbo].[CustomerSeller] AS [Extent2] ON EXISTS (SELECT
1 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable1]
LEFT OUTER JOIN (SELECT
[Extent3].[Id] AS [Id]
FROM [dbo].[Customer] AS [Extent3]
WHERE [Extent1].[ReceivedFrom_Id] = [Extent3].[Id]) AS [Project1] ON 1 = 1
LEFT OUTER JOIN (SELECT
[Extent4].[Id] AS [Id]
FROM [dbo].[Customer] AS [Extent4]
WHERE [Extent2].[Customer_Id] = [Extent4].[Id]) AS [Project2] ON 1 = 1
LEFT OUTER JOIN (SELECT
[Extent5].[Id] AS [Id]
FROM [dbo].[Customer] AS [Extent5]
WHERE [Extent1].[ReceivedFrom_Id] = [Extent5].[Id]) AS [Project3] ON 1 = 1
LEFT OUTER JOIN (SELECT
[Extent6].[Id] AS [Id]
FROM [dbo].[Customer] AS [Extent6]
WHERE [Extent2].[Customer_Id] = [Extent6].[Id]) AS [Project4] ON 1 = 1
WHERE ([Project1].[Id] = [Project2].[Id]) OR (([Project3].[Id] IS NULL) AND ([Project4].[Id] IS NULL))
)
WHERE [Extent2].[Seller_Id] = @p__linq__0}
我什麼格式錯誤我的LINQ查詢(我是新)特別感興趣..怎麼寫呢?或者我的實體結構有問題嗎?或者SQL很好,並且可以在生產環境中高效運行大量數據?
進展情況:
首先,Slauma提出了兩個新的查詢。在對它們進行實驗時,我注意到SQL在處理Customer和Customer字段的連接表CustomerSeller上付出了額外的努力。這是我的錯誤,因爲這些字段應該是不可空的。我將它們設置爲[必填]在實體:
public class CustomerSeller {
public int Id { get; set; }
[Required]
public Seller Seller { get; set; }
[Required]
public Customer Customer { get; set; }
public decimal CommissionRate { get; set; }
}
第一個查詢Slauma寫道:
var qry = from p in db.Payments
where p.ReceivedFrom.CustomerSellers.Any(cs => cs.Seller.Id == id)
select p;
產生了大大優於SQL:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Date] AS [Date],
[Extent1].[Amount] AS [Amount],
[Extent1].[ReceivedFrom_Id] AS [ReceivedFrom_Id]
FROM [dbo].[Payment] AS [Extent1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [dbo].[CustomerSeller] AS [Extent2]
WHERE ([Extent1].[ReceivedFrom_Id] = [Extent2].[Customer_Id]) AND ([Extent2].[Seller_Id] = @p__linq__0)
)
下TWEAK Slauma建議是加入我的原始查詢的Id字段,而不是整個實體:
var qry = from p in db.Payments
//old: join cs in db.CustomerSellers on p.ReceivedFrom equals cs.Customer
//new:
join cs in db.CustomerSellers on p.ReceivedFrom.Id equals cs.Customer.Id
where cs.Seller.Id == id
select p;
這會產生一個非常高品質的SQL語句:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Date] AS [Date],
[Extent1].[Amount] AS [Amount],
[Extent1].[ReceivedFrom_Id] AS [ReceivedFrom_Id]
FROM [dbo].[Payment] AS [Extent1]
INNER JOIN [dbo].[CustomerSeller] AS [Extent2] ON [Extent1].[ReceivedFrom_Id] = [Extent2].[Customer_Id]
WHERE [Extent2].[Seller_Id] = @p__linq__0
本質是什麼,我會在SQL直接寫我自己。
小貼士:
(1)加入上的按鍵,而不是完整的實體。 (2)如果連接表不能包含多對多映射字段的空值,請將它們標記爲[必需],這會簡化查詢。 (3)檢查linq查詢的幕後SQL,特別是在經常使用或者他們可能會觸及大量數據的情況下。可能有怪物躲藏。
(4)Slauma是一位紳士和學者。:-)
說實話 - 它看起來很亂 - 你看過查詢計劃還是你寫的查詢? – Charleh