Обходной путь, необходимый для EF Core, выполняющего операции GroupBy в памяти, а не в SQL.

Я работаю в Entity Framework Core 1.1.0 (и на данный момент обновление недоступно из-за критических изменений в более поздних версиях). Мой запрос имеет следующий вид:

var q = db.MyTable
            .GroupBy(t => new { t.Field1 })
            .Select(g => new
            {
                g.Key.Field1,
                MaxField2 = g.Max(x => x.Field2)
            })
            .ToList();

В тестовом коде это работает хорошо и возвращает ожидаемые данные. Но при развертывании в реальной среде с реальными данными время ожидания истекает. Почему? Итак, я поставил сниффер на SQL-сервер, и вот собственно SQL:

SELECT [t].[Field1], [t].[Field2], [t].[Field3], [t].[Field4], [t].[Field5]
FROM [dbo].[MyTable] AS [t]
ORDER BY [t].[Field1]

Ой. Ну это бы объяснило. EF только компилирует запрос до .GroupBy() в SQL, таким образом пытаясь загрузить все содержимое таблицы (около 17 миллионов записей на момент написания) в память, а остальную часть группировки и порядок должен быть сделан в памяти.

Любые предложения, как переработать этот запрос, чтобы тяжелая работа выполнялась в SQL?


person Shaul Behr    schedule 19.07.2017    source источник
comment
почему бы вам не сделать свой запрос более читабельным, чтобы избежать исключений? использовать переменные Expression<Func<>> и работать с ними. Я не думаю, что кто-то захочет перестраивать вашу структуру БД с нуля, работать со старой версией EF Core и полностью отлаживать ее за вас.   -  person Matthias Burger    schedule 19.07.2017
comment
@MatthiasBurger смотрите мое обновление.   -  person Shaul Behr    schedule 19.07.2017
comment
@ShaulBehr Вы тестировали этот сокращенный запрос? Он показывает ту же проблему?   -  person xanatos    schedule 19.07.2017
comment
@xanatos, да, я протестировал сокращенный запрос. Такое же поведение.   -  person Shaul Behr    schedule 19.07.2017
comment
Если вам нужны такие запросы, EF Core — самая большая ошибка, которую вы совершили :( Никаких обходных путей, что бы вы ни пробовали, либо генерируется исключение, либо выполняется в памяти. И не поддерживается произвольный SQL-запрос.   -  person Ivan Stoev    schedule 19.07.2017
comment
@IvanStoev не совсем точно. Я нашел обходной путь; см. мой ответ.   -  person Shaul Behr    schedule 19.07.2017
comment
Повезло тебе! Пока вам не понадобится другой агрегат, например Sum :)   -  person Ivan Stoev    schedule 19.07.2017
comment
@IvanStoev (неловко переминается с ноги на ногу) эм... ну... 8-\   -  person Shaul Behr    schedule 19.07.2017


Ответы (3)


Как указывает @xanatos, это не поддерживается в EF Core 1.1.0 (и даже не 2.0.0). Однако есть обходной путь, использующий буквальный SQL:

var q = db.MyTable
        .FromSql("select t.* from " +
                 "  (select distinct Field1 from MyTable) t0 " +
                 "cross apply " +
                 "  (select top 1 t.* from MyTable t " +
                 "  where t.Field1 = t0.Field1 " +
                 "  order by t.Field2 desc) t")                     
        .Select(t => new
        {
            t.Field1,
            MaxField2 = t.Field2
        })
        .ToList();

Не то решение, на которое я надеялся, но оно работает.

person Shaul Behr    schedule 19.07.2017

Не поддерживается в EF Core 1.1.0: https://github.com/aspnet/EntityFramework/issues/2341

Операторы LINQ GroupBy() иногда можно преобразовать в предложения SQL GROUP BY, в частности, когда в проекции применяются агрегатные функции.

К сожалению, он не будет поддерживаться даже в EF Core 2.0.0.

person xanatos    schedule 19.07.2017
comment
Для более полного списка того, что уже есть или нет. Взгляните на эту ссылку. docs.microsoft.com/en-us/ef/efcore- и-ef6/функции - person Jordy van Eijk; 19.07.2017

Как видно из этой записи блога, GROUP BY будет поддерживается в версии 2.1, которая еще не выпущена, но ожидается в первом и втором кварталах 2018 г. 4 квартал 2017 г..

person JotaBe    schedule 06.09.2017
comment
Кажется, они перенесли выпуск 2.1 на Q1-Q2 2018 :( - person ThunderDev; 13.11.2017