[英]Is it possible to hint at EntityFramework Core to use an inner join instead of an EXISTS sub-query for navigation property collection conditions?
我有四個表:Users、Songs、Genres 和 FavoriteGenres。
用戶可以選擇最喜歡的流派,當他們導航到他們的儀表板時,他們會看到一個屬於他們最喜歡的流派的歌曲列表。
我有以下 EF 核心代碼來完成此操作:
dbContext
.Songs
.Include(song => song.Genre)
.Where(song => song.Genre.FavoriteGenres.Any(favoriteGenre => favoriteGenre.UserId == userId))
.OrderBy(song => song.CreatedDateTimeOffset)
.ToListAsync(cancellationToken);
output(工作正常)的查詢是:
SELECT [s].[Id], [s].[Title], [s].[CreatedDateTimeOffset], [g].[Id], [g].[Name]
FROM [Songs] AS [s]
INNER JOIN [Genres] AS [g] ON [s].[GenreId] = [g].[Id]
WHERE EXISTS (
SELECT 1
FROM [FavoriteGenres] AS [f]
WHERE ([g].[Id] = [f].[GenreId]) AND ([f].[UserId] = @__userId_0))
ORDER BY [s].[CreatedDateTimeOffset] DESC
我只是想知道是否可以暗示 EF 核心生成以下查詢,我認為這可能更優化(盡管我很想知道我錯了:):
SELECT [s].[Id], [s].[Title], [s].[CreatedDateTimeOffset], [g].[Id], [g].[Name]
FROM [Songs] AS [s]
INNER JOIN [Genres] AS [g] ON [s].[GenreId] = [g].[Id]
INNER JOIN [FavoriteGenres] AS [f] ON [f].[GenreId] = [g].[Id]
WHERE [f].[UserId] = @__userId_0
ORDER BY [s].[CreatedDateTimeOffset] DESC
執行計划看起來非常相似,但老實說,我不是閱讀它們的專家。 任何形式的幫助將不勝感激:)
雖然@IvanStoev的評論是正確的並且沒有必要重寫查詢,但如果您仍然關心不同的查詢生成,一個簡單的方法是重新排序您的表包含。 由於您最終不會將歌曲作為根實體,而是喜歡的流派,因此您可能希望重新排列返回的實體:
var result = context.FavoriteGenres
.Include(favoriteGenre => favoriteGenre.Genre)
.ThenInclude(genre => genre.Songs)
.Where(favoriteGenre => favoriteGenre.UserId == userId)
.AsEnumerable() // <-- switch to client-evaluation to return songs instead of favorite genres
.SelectMany(favoriteGenre => favoriteGenre.Genre.Songs)
.OrderBy(song => song.CreatedDateTimeOffset)
.ToList();
這將導致生成以下 SQL:
SELECT [f].[UserId], [f].[GenreId], [g].[Id], [g].[Name], [s].[Id], [s].[CreatedDateTimeOffset], [s].[GenreId], [s].[Title]
FROM [FavoriteGenres] AS [f]
INNER JOIN [Genres] AS [g] ON [f].[GenreId] = [g].[Id]
LEFT JOIN [Songs] AS [s] ON [g].[Id] = [s].[GenreId]
WHERE [f].[UserId] = @__userId_0
ORDER BY [f].[UserId], [f].[GenreId], [g].[Id], [s].[Id]
EXISTS 幾乎總是性能更高,但是, SQL 服務器足夠聰明,可以在大多數情況下為兩個查詢字符串生成相同的查詢計划,因此您只需更改發送到 SQL 服務器的文本,但不會真正影響查詢計划一旦到達那里就會生成。
但是,如果您真的想要,您可以使用.Include
of the Favorites 讓它生成您想要的查詢,然后如果您不希望結果中的 Favorite 列.Select
只有您想要的列像:
dbContext
.Songs
.Include(song => song.Genre)
.ThenInclude(genre => genre.FavoriteGenres)
.Where(song => song.Genre.FavoriteGenres.Any(favoriteGenre => favoriteGenre.UserId == userId))
.Select(song => new {
song.Id,
song.Title,
song.Genre.Id,
song.Genre.Name
})
.OrderBy(song => song.CreatedDateTimeOffset)
.ToListAsync(cancellationToken);
要么
dbContext
.Songs
.Include(song => song.Genre)
.ThenInclude(genre => genre.FavoriteGenres.Select(favoriteGenre => favoriteGenre.UserId == userID)
.Select(song => new {
song.Id,
song.Title,
song.Genre.Id,
song.Genre.Name
})
.OrderBy(song => song.CreatedDateTimeOffset)
.ToListAsync(cancellationToken);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.