powered by simpleCommunicator - 2.0.33     © 2025 Programmizd 02
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / Неоптимальность генерируемого EF Core sql-запроса
11 сообщений из 11, страница 1 из 1
Неоптимальность генерируемого EF Core sql-запроса
    #39925059
vb_sub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Всем привет, есть
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
    /// <summary>
    /// Базовый класс накладной  с основными атрибутами
    /// </summary>
    public class BaseInvoice
    {

        /// <summary>
        /// Идентификатор накладной
        /// </summary>
        public Guid ID { get; set; }

        /// <summary>
        /// Номер накладной
        /// </summary>
        public int InvoiceNumber { get; set; }


        /// <summary>
        /// Дата создания накладной
        /// </summary>
        public DateTime CreationDate { get; set; }


        /// <summary>
        /// История напечатанных накладных
        /// </summary>
        public ICollection<Invoice> Invoices { get; set; }


        /// <summary>
        /// Идентификатор пользователя, совершившего операцию
        /// </summary>
        public string UserId { get; set; }

        public BaseInvoice()
        {
            Invoices = new List<Invoice>();
        }
    }


есть сама накладная
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
 /// <summary>
    /// Накладная на перемещение  товара
    /// </summary>
    public  class Invoice
    {
        /// <summary>
        /// Идентификатор накладной
        /// </summary>
        public Guid ID { get; set; }


        /// <summary>
        /// Склад-отправитель для накладной 
        /// </summary>
        public WareHouse Sender { get; set; }


        /// <summary>
        /// Склад-получатель для накладной 
        /// </summary>
        public WareHouse Receiver  { get; set; }


        /// <summary>
        /// Список товаров, отпущенной по накладной
        /// </summary>
        public ICollection<InvoiceCommodity> Commodities { get; set; }

        /// <summary>
        /// Базовая накладная
        /// </summary>
        public BaseInvoice BaseInvoice { get; set; }

        /// <summary>
        /// Дата создания накладной
        /// </summary>
        public DateTime CreationDate { get; set; }


        /// <summary>
        /// Идентификатор пользователя, совершившего операцию
        /// </summary>
        public string UserId { get; set; }
    }


Смысл такой архитектуры в том, что есть базовые характеристики накладной, которые не меняются после её создания- номер, первоначальный создатель, а есть изменяющаяся информация, которая прикреплена к этой накладной (набор товаров, отправитель, получатель и тд). Изменяющаяся она потому что содержание накладной можно пересоздавать- например пользователь заметил, что неправильно создал её и хочет перепечатать с новым набором товаров. Но при этом, все напечатанные версии накладной должны храниться и актуальной является самая последняя по дате создания (CreationDate).
У меня возникает необходимость вывести список накладных с содержанием.

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
   var printedInvoices =await mapplicationDbContext
                         .BaseInvoices
                         .AsNoTracking()
                          .Select(t => new PrintedInvoicesResponse
                          {
                              id = t.ID,
                              creationDate = t.Invoices.OrderBy(g => g.CreationDate).Last().CreationDate,
                              invoiceNumber = t.InvoiceNumber,                              
                              receiverWarehouseId=t.Invoices.OrderBy(g=>g.CreationDate).Last().Receiver.ID,
                              receiverWarehouseName = t.Invoices.OrderBy(g => g.CreationDate).Last().Receiver.Name,
                              senderWarehouseId= t.Invoices.OrderBy(g => g.CreationDate).Last().Sender.ID,
                              senderWarehouseName= t.Invoices.OrderBy(g => g.CreationDate).Last().Sender.Name
                          })
                         .ToListAsync();


EF Core в транслирует Linq в жутко неоптимальный sql-запрос
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
SELECT [b].[ID] AS [id], (
    SELECT TOP(1) [i].[CreationDate]
    FROM [Invoices] AS [i]
    WHERE [b].[ID] = [i].[BaseInvoiceID]
    ORDER BY [i].[CreationDate] DESC) AS [creationDate], [b].[InvoiceNumber] AS [invoiceNumber], (
    SELECT TOP(1) [w].[ID]
    FROM [Invoices] AS [i0]
    LEFT JOIN [WareHouses] AS [w] ON [i0].[ReceiverID] = [w].[ID]
    WHERE [b].[ID] = [i0].[BaseInvoiceID]
    ORDER BY [i0].[CreationDate] DESC) AS [receiverWarehouseId], (
    SELECT TOP(1) [w0].[Name]
    FROM [Invoices] AS [i1]
    LEFT JOIN [WareHouses] AS [w0] ON [i1].[ReceiverID] = [w0].[ID]
    WHERE [b].[ID] = [i1].[BaseInvoiceID]
    ORDER BY [i1].[CreationDate] DESC) AS [receiverWarehouseName], (
    SELECT TOP(1) [w1].[ID]
    FROM [Invoices] AS [i2]
    LEFT JOIN [WareHouses] AS [w1] ON [i2].[SenderID] = [w1].[ID]
    WHERE [b].[ID] = [i2].[BaseInvoiceID]
    ORDER BY [i2].[CreationDate] DESC) AS [senderWarehouseId], (
    SELECT TOP(1) [w2].[Name]
    FROM [Invoices] AS [i3]
    LEFT JOIN [WareHouses] AS [w2] ON [i3].[SenderID] = [w2].[ID]
    WHERE [b].[ID] = [i3].[BaseInvoiceID]
    ORDER BY [i3].[CreationDate] DESC) AS [senderWarehouseName]
FROM [BaseInvoices] AS [b]


То есть формируется много паразитных LEFT JOIN, хотя достаточно всего лишь 2.
Понятно, что это из-за того, что я тянусь к вложенным свойствам t.Invoices.OrderBy(g=>g.CreationDate).Last().
Можно как-либо в секции Select замутить что-то вроде
Код: c#
1.
2.
3.
4.
5.
6.
///linq
.select {
var invoice=t.Invoices.OrderBy(g=>g.CreationDate).Last();
receiverWarehouseId=invoice..Receiver.ID,
///аналогично
}


что бы EF Core не генирировал дублирующие left join, потому что на нормальном количестве данных такой запрос будет тормозить.
Спасибо
...
Рейтинг: 0 / 0
Неоптимальность генерируемого EF Core sql-запроса
    #39925063
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ничто не мешает написать join ручками в linq-запросе
...
Рейтинг: 0 / 0
Неоптимальность генерируемого EF Core sql-запроса
    #39925069
vb_sub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.Pro,
Вы имеете ввиду FromSqlRaw?
...
Рейтинг: 0 / 0
Неоптимальность генерируемого EF Core sql-запроса
    #39925083
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vb_sub
EF Core в транслирует Linq в жутко неоптимальный sql-запрос


Это не запрос "не оптимальный".
Это модель данных ущербная.

SQL конечно способен вытянуть данные быстро из любого г-на.
...
Рейтинг: 0 / 0
Неоптимальность генерируемого EF Core sql-запроса
    #39925085
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vb_sub
Shocker.Pro,
Вы имеете ввиду FromSqlRaw?
нет, я имею ввиду именно join из linq
...
Рейтинг: 0 / 0
Неоптимальность генерируемого EF Core sql-запроса
    #39925096
vb_sub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
hVostt
vb_sub
EF Core в транслирует Linq в жутко неоптимальный sql-запрос


Это не запрос "не оптимальный".
Это модель данных ущербная.

SQL конечно способен вытянуть данные быстро из любого г-на.


Как должен выглядеть неущербный вариант?
...
Рейтинг: 0 / 0
Неоптимальность генерируемого EF Core sql-запроса
    #39925128
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vb_sub
Как должен выглядеть неущербный вариант?


Моделируйте прежде всего с точки зрения бизнеса.

Для примера, как вариант:

У вас должна быть лишь сущность накладной, без разбивки на "изменяемую" и "неизменяемую" части -- таких понятий в бизнесе нет.

Но вам нужна также история, ведите её отдельно. Таким образом, чтобы наличие или отсутствие истории никак не влияло на основную логику.

Получается, что у вас будет

1. Invoice -- полная сущность накладной.
2. InvoiceHistory -- архивная сущность накладной.

Хотите работать с историей, работаете с InvoiceHistory.
Хотите работать с текущими актуальными данными, работаете с Invoice.

Посмотрите, как это положительно скажется, на размере, скорости работы запросов, а также вы уберёте лишние грабли, на которые очень легко наступить в вашей схеме.
...
Рейтинг: 0 / 0
Неоптимальность генерируемого EF Core sql-запроса
    #39925334
fkthat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Судить о том, насколько запрос "оптимальный" или "неоптимальный" можно только по его плану выполнения на sql-сервере, а никак не по тексту самого запроса.
...
Рейтинг: 0 / 0
Неоптимальность генерируемого EF Core sql-запроса
    #39925441
Фотография hVostt
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
fkthat
Судить о том, насколько запрос "оптимальный" или "неоптимальный" можно только по его плану выполнения на sql-сервере, а никак не по тексту самого запроса.


Как общий подход -- да, так. Но у запросов могут быть другие проблемы, кроме плана. И по-серьёзней. Текст смотреть тоже нужно.
...
Рейтинг: 0 / 0
Неоптимальность генерируемого EF Core sql-запроса
    #39925471
vb_sub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
fkthat
Судить о том, насколько запрос "оптимальный" или "неоптимальный" можно только по его плану выполнения на sql-сервере, а никак не по тексту самого запроса.

Согласен, тут более подходит определение "избыточен", чем "неоптимален"
...
Рейтинг: 0 / 0
Неоптимальность генерируемого EF Core sql-запроса
    #39925503
fkthat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vb_sub
Согласен, тут более подходит определение "избыточен", чем "неоптимален"

А что в этом за печаль - ты же не руками этот запрос пишешь. Теоретически, запрос может быть плохой даже при хорошем плане, если он какой-то такой, что не позволяет этот план серверу закешировать, чего-то другого мне на ум так сразу не приходит. Но, сам сиквел уже года по-моему чуть ли не с 2005 с этим успешно справляется (самостоятельно выносит литералы из запроса в параметры).
...
Рейтинг: 0 / 0
11 сообщений из 11, страница 1 из 1
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / Неоптимальность генерируемого EF Core sql-запроса
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]