我有以下 LINQ 查詢(使用 EF Core 6 和 MS SQL Server):
var resultSet = dbContext.Systems
.Include(system => system.Project)
.Include(system => system.Template.Type)
.Select(system => new
{
System = system,
TemplateText = system.Template.TemplateTexts.FirstOrDefault(templateText => templateText.Language == locale.LanguageIdentifier),
TypeText = system.Template.Type.TypeTexts.FirstOrDefault(typeText => typeText.Language == locale.LanguageIdentifier)
})
.FirstOrDefault(x => x.System.Id == request.Id);
要求是檢索與請求的 ID 匹配的系統并加載其專案、模板和模板的型別資訊。該模板有多個 TemplateTexts(每種翻譯語言一個),但我只想加載與請求的語言環境匹配的一個,同樣處理模板型別的 TypeTexts 元素。
上面的 LINQ 查詢在一個查詢中執行此操作,它被轉換為以下 SQL 查詢(我編輯了 SELECT 陳述句以使用 * 而不是生成的長列串列):
SELECT [t1].*, [t2].*, [t5].*
FROM (
SELECT TOP(1) [p].*, [t].*, [t0].*
FROM [ParkerSystems] AS [p]
LEFT JOIN [Templates] AS [t] ON [p].[TemplateId] = [t].[Id]
LEFT JOIN [Types] AS [t0] ON [t].[TypeId] = [t0].[Id]
LEFT JOIN [Projects] AS [p0] ON [p].[Project_ProjectId] = [p0].[ProjectId]
WHERE [p].[SystemId] = @__request_Id_1
) AS [t1]
LEFT JOIN (
SELECT [t3].*
FROM (
SELECT [t4].*, ROW_NUMBER() OVER(PARTITION BY [t4].[ReferenceId] ORDER BY [t4].[Id]) AS [row]
FROM [TemplateTexts] AS [t4]
WHERE [t4].[Language] = @__locale_LanguageIdentifier_0
) AS [t3]
WHERE [t3].[row] <= 1
) AS [t2] ON [t1].[Id] = [t2].[ReferenceId]
LEFT JOIN (
SELECT [t6].*
FROM (
SELECT [t7].*, ROW_NUMBER() OVER(PARTITION BY [t7].[ReferenceId] ORDER BY [t7].[Id]) AS [row]
FROM [TypeTexts] AS [t7]
WHERE [t7].[Language] = @__locale_LanguageIdentifier_0
) AS [t6]
WHERE [t6].[row] <= 1
) AS [t5] ON [t1].[Id0] = [t5].[ReferenceId]
這還不錯,它不是一個超級復雜的查詢,但我覺得我的要求可以用一個更簡單的 SQL 查詢來解決:
SELECT *
FROM [Systems] AS [p]
JOIN [Templates] AS [t] ON [p].[TemplateId] = [t].[Id]
JOIN [TemplateTexts] AS [tt] ON [p].[TemplateId] = [tt].[ReferenceId]
JOIN [Types] AS [ty] ON [t].[TypeId] = [ty].[Id]
JOIN [TemplateTexts] AS [tyt] ON [ty].[Id] = [tyt].[ReferenceId]
WHERE [p].[SystemId] = @systemId and tt.[Language] = 2 and tyt.[Language] = 2
我的問題是:是否有一個不同/更簡單的 LINQ 運算式(在方法語法或查詢語法中)產生相同的結果(一次獲取所有資訊),因為理想情況下我不希望有一個匿名物件過濾的子集合被聚合。對于更多的布朗尼點,如果生成的 SQL 更簡單/更接近我認為的簡單查詢,那就太好了。
uj5u.com熱心網友回復:
是否有產生相同結果的不同/更簡單的 LINQ 運算式 (...)
是(也許)和不。
不,因為您正在查詢dbContext.Systems,因此 EF 將回傳與您的過濾器匹配的所有系統,即使它們沒有TemplateTexts等。這就是它必須生成外連接的原因。EF 不知道您明顯打算跳過沒有這些嵌套資料的系統,也不知道這些系統不會出現在資料庫中。(你似乎假設,看到第二個查詢)。
這說明了子查詢的左連接。
這些子查詢是因為FirstOrDefault. 在 SQL 中,它總是需要某種子查詢來獲取一對多關系的“第一條”記錄。這種ROW_NUMBER() OVER結構實際上是非常有效的。您的第二個查詢沒有任何“第一”記錄的概念。它可能會回傳不同的資料。
是的(也許)因為你也有Include資料。我不確定為什么。有些人似乎認為Include有必要使后續預測 ( .Select) 起作用,但事實并非如此。如果這是您使用Includes 的原因,那么您可以洗掉它們,從而洗掉前幾個聯接。
OTOH你也Include system.Project沒有在投影中,所以你似乎Include故意添加了s。在這種情況下,它們會起作用,因為整個物體system都在投影中,否則 EF 會忽略它們。
如果您Include再次需要 s,則 EF 必須出于上述原因生成外連接。
EF 決定分別處理Includes 和預測,而手工制作的 SQL 在資料的先驗知識的幫助下可以更有效地做到這一點。不過,沒有辦法影響這種行為。
uj5u.com熱心網友回復:
這個 LINQ 查詢接近你的 SQL,但我擔心結果的正確性:
var resultSet =
(from system in dbContext.Systems
from templateText in system.Template.TemplateTexts
where templateText.Language == locale.LanguageIdentifier
from typeText in system.Template.Type.TypeTexts
where typeText.Language == locale.LanguageIdentifier
select new
{
System = system,
TemplateText = templateText
TypeText = typeText
})
.FirstOrDefault(x => x.System.Id == request.Id);
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/411340.html
標籤:
