我一直在清理一些实体LINQ方法查询,以尝试优化项目中生成的查询,并试图通过外键的GROUP BY
来避免JOIN
。但在实体框架中遇到了意外的投影失败。
使用LINQPAD7中的演示数据库,这里有一个原始查询的例子,以及我如何尝试优化它
作品:
Albums
.Select(a => a.Tracks.OrderBy(t => t.Milliseconds).First())
.Select(g => new { g.Name, g.Composer })
.Dump();
运行时异常:
Tracks.GroupBy(t => t.AlbumId)
.Select(g => g.OrderBy(t => t.Milliseconds).First())
.Select(g => new { g.Name, g.Composer })
.Dump();
现在我知道我可以执行以下操作,但它生成了一个非常奇怪的SQL查询(连接到自己(,而且读起来不像失败的示例那样干净。
Tracks.GroupBy(t => t.AlbumId)
.Select(g => g.OrderBy(t => t.Milliseconds).Select(g => new { g.Name, g.Composer }).First())
.Dump();
有人能解释为什么实体框架不喜欢后面跟着选择和投影的GroupBy
吗?与表类似的联接是否有效?
我正在寻找的最佳查询是:
SELECT Name, Composer
FROM (
SELECT Name, Composer, ROW_NUMBER() OVER(PARTITION BY AlbumId ORDER BY Milliseconds) AS row
FROM Track
)
WHERE row <= 1
我注意到,如果您更改查询顺序,则不会发生异常。。。
更改此项:
Tracks
.GroupBy(t => t.AlbumId)
.Select(g => g.OrderBy(t => t.Milliseconds).First())
.Select(g => new { g.Name, g.Composer })
.Dump();
有了这个:
Tracks
.OrderBy(x => x.Milliseconds)
.GroupBy(x => x.AlbumId)
.Select(x => new {
x.FirstOrDefault().Name,
x.FirstOrDefault().Composer
})
我认为这是因为你试图在组中对列表进行排序,而你应该先对所有结果进行排序,然后再进行分组,尽管我不完全确定。如果不是linq到sql,这将非常有效。