Выбор одного значения из каждой группы в таблице БД при помощи LINQ и Entity Framework
Есть таблица в БД с примерно такой структурой:
table
(
id int,
field1 varchar,
field2 datetime
)
Задача - выбрать для каждого уникального значения field1 самую новую запись (по полю field2). В чистом SQL обычно делаю так:
select *
from
(
select *,
row_number() over(partition by t.field1 order by t.field2 desc)
from table as t
) as q
where q.rn = 1
Подскажите, пожалуйста, как сделать что-то подобное на LINQ? Я пробовал выбирать так:
var topList = list.GroupBy(l => l.field1, (key, g) => g.OrderByDescending(l => l.field2).First()).ToList();
Работает, но только на локальных списках, в MS SQL не транслируется (выдаёт ошибку трансляции).
Точно работает
var topList = list
.Where(l => l.Id ==
list.Where(l2 => l2.field1 == j.field1)
.OrderByDescending(j2 => j2.field2)
.First().Id)
.ToList();
Но оно транслируется в
select *
from table as t
where id = (select top 1 id
from table as t2
where t2.field1 = t1.field1
order by field2)
что, по-моему, некрасиво и неоптимально.
Используется .Net Framework 4.6.1, Entity Framework Core 3, MS SQL 2014.
Update
Решение из https://stackoverflow.com/questions/9980568/row-number-over-partition-by-xxx-in-linq не подходит, так как, видимо, не поддерживается EF и не транслируется в SQL.
Ответы (2 шт):
Оконные функции не поддерживаются ни LINQ, ни транслятором EF. Тем не менее, вы не первый, кто озадачился таким вопросом и эта фича уже есть в бэклоге.
Относительно же вашей ситуации можно посоветовать 2 варианта:
- Используйте то, что уже есть, если оно выполняет вашу задачу. То, во что оно транслируется, вовсе необязательно может быть плохо. Посмотрите ближе, сравните планы. Если всё не так плохо, то оставьте. Если же вариант с окнами выполняется значительно эффективнее или ненамного эффективнее, но этот запрос является узким местом, то используйте:
- Нативный запрос.
Я бы посоветовал вместо EF Core использовать гораздо более мощную и гибкую ORM LINQ to DB. В частности, в ней реализована полная поддержка оконных функций.
Если страшно вот так сразу отказаться от EF (ведь это разработка самой Microsoft!), то советую попробовать библиотеку linq2db.EntityFrameworkCore. Она позволит использовать все возможности linq2db в EF Core.
Эта библиотека находится в списке EF Core Tools & Extensions. Там можно и другие полезные расширения найти. А "голый" EF весьма уныл.