簡體   English   中英

是否可以暗示 EntityFramework Core 使用內部連接而不是 EXISTS 子查詢來獲取導航屬性集合條件?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM