Приостановленный запрос на выборку при вызове ядром C # EF, MSSQL management studio выполняет запрос очень хорошо. Почему?

2

Я не могу дать вам достаточно данных, чтобы воспроизвести ошибки, поэтому я собираюсь предоставить вам столько данных, сколько смогу сойти с рук.

У меня есть оператор выбора, выполняющийся из ядра EF.

var bookings = context.Booking
                      .Where(booking => booking.ConsigneeNumber == customer.GetCustomerTarget().Code 
                             && booking.CreatedAt >= from 
                             && booking.CreatedAt < to 
                             && booking.BookingLine.Any(b => b.BookingLineSpecification
                                                        .Any(c => c.CurrencyCode == code))
                            )

                     .Include(booking => booking.BookingLine)
                         .ThenInclude(bl => bl.BookingLineSpecification)
                         .ThenInclude(bls => bls.UnitType)
                     .Include(booking => booking.BookingLine)
                         .ThenInclude(bl => bl.BookingLineAddress)
                         .ThenInclude(bla => bla.Country)
                     .Include(booking => booking.BookingLine)
                         .ThenInclude(bl => bl.BookingLineAddress)
                         .ThenInclude(bla => bla.PostalCode)
                     .Include(booking => booking.BookingLine)
                         .ThenInclude(bl => bl.BookingLineSpecification)
                         .ThenInclude(bls => bls.RelBookingLineSpecificationSalesInvoiceDetail)
                         .ThenInclude(Rel => Rel.SalesInvoiceDetail);

Сам SQL-запрос, приостановленный на сервере MSSQL, становится:

     (@__GetCustomerTarget_Code_0 bigint,@__from_1 datetime2(7),@__to_2 datetime2(7),@__code_3 varchar(255))
        SELECT [booking].[Id], 
    [booking].[booking_provider_id], 
    [booking].[booking_status_id], 
    [booking].[consignee_name], 
    [booking].[consignee_number], 
    [booking].[created_at], 
    [booking].[created_by], 
    [booking].[currency_code], 
    [booking].[deliveryNumber], 
    [booking].[description], 
    [booking].[destroyed_at], 
    [booking].[destroyed_by], 
    [booking].[inter_company_number], 
    [booking].[invoicee_name], 
    [booking].[invoicee_number], 
    [booking].[is_create], 
    [booking].[location_id], 
    [booking].[location_name], 
    [booking].[maturity_level_id], 
    [booking].[number], 
    [booking].[order_number], 
    [booking].[provider_key], 
    [booking].[shipment_id], 
    [booking].[system_responsible_id], 
    [booking].[updated_at], 
    [booking].[updated_by]  
    FROM [Integration].[booking] AS [booking]  
WHERE ((([booking].[consignee_number] = @__GetCustomerTarget_Code_0) 
AND ([booking].[created_at] >= @__from_1)) 
AND ([booking].[created_at] < @__to_2)) 
AND EXISTS (      
SELECT 1      
FROM [Integration].[booking_line] AS [b]      
WHERE EXISTS (          
SELECT 1          
FROM [Integration].[booking_line_specification] AS [c]          
WHERE ([c].[currency_code] = @__code_3) AND ([b].[Id] = [c].[booking_line_id])) AND ([booking].[Id] = [b].[booking_id]))

Этот оператор выполняется за ноль секунд (но несколько миллисекунд) при выполнении в MSSQL management studio. Однако приложение С# испытывает таймаут.

Когда я использую внутренние инструменты на MSSQL, я вижу, что spid приостановлен и постоянно ожидает. Однако причина, кажется, меняется. В начале это связано с IO_COMPLETION. Затем его SOS_YIELD_ ~ что-то и, наконец, PAGEIOLATCH_SH Это окончательное состояние, в котором он остается

Я, на всю жизнь, не могу понять, почему MSSQL может выполнить запрос без каких-либо проблем. Но EF, по-видимому, не в состоянии использовать индексы. Или что-то еще, чего мне не хватает полностью.

У меня просто нет идей. Кто-нибудь может указать мне направление, которое может помочь?

Я пытался:

Запуск в Visual Stuido 2017. Запуск в режиме релиза. Я попытался включить ленивый режим, а не использовать включает. Я попытался удалить ленивые грузы и включения, просто чтобы посмотреть, смогу ли я вернуть заказы.

Нету. Кажется, MSSQL отказывает EF Core в использовании индексов.

Дело в том, что запрос зависает только тогда, когда я предоставляю определенные параметры. Остальные параметры работают просто отлично. В частности, если я предоставлю разные коды валют, это, кажется, будет иметь значение для MSSQL, если запрос будет приостановлен или нет.

Я полностью перестроил индексы, необходимые для эффективного выполнения этого запроса в соответствии с планом выполнения в MSSQL management studio.

Любая дополнительная информация, которая может потребоваться, пожалуйста, дайте мне знать, и я посмотрю, что я могу сделать, в меру своих усилий.

ОБНОВЛЕНИЕ Фактический план выполнения:

Изображение 174551

ОБНОВЛЕНИЕ 2: Я хотел бы указать, что это в настоящее время используется для разработки, и, таким образом, эта БД, мое программное обеспечение и все, что между ними, находится под моим "контролем".

Насколько мой явно неопытный ум может контролировать все, что угодно :)

Таким образом, любые предложения о том, как лучше отладить проблему, или запросы на дополнительные данные будут встречены с энергией и благодарностью. И, скорее всего, будет возможно, особенно если намекнуть, как это вам предоставить! (И я)

SQL Profiler: открытие соединения с БД:

set quoted_identifier on
set arithabort on
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat mdy
set datefirst 7
set transaction isolation level read committed
  • 0
    @DenisRubashkin Я попытался определить все параметры, как это: .HasColumnType ("varchar") Без изменений.
Показать ещё 15 комментариев
Теги:
sql-server
entity-framework-core
ef-core-2.2

2 ответа

2
Лучший ответ

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

Мы выполняем эту хранимую процедуру до фактического выполнения нашего кода на С#, который запрашивает эти конкретные данные.

Это ищет кэшированные планы выполнения, которые можно использовать для запроса, который очень похож на тот, который генерирует EF. И тогда это убивает их одного за другим.

Это не очень хорошая вещь. 1, он требует от пользователя иметь разрешения для этого. Ни одно приложение не должно иметь такой уровень разрешений. Вы можете утверждать, что все, что вы хотите, "могло бы иметь", но на самом деле вы все знаете, что этого не должно быть. 2,
В коде я выполняю хранимую процедуру перед оператором выбора, чтобы заставить приложение выполнить. В самом деле. В самом деле? Ну да. Я. Это аккуратно? нет. Это чисто? нет. Я бы даже не рекомендовал делать это как таковое. В этом есть много возможных подводных камней, если вы измените параметры или измените запрос каким-либо образом, вам придется перестроить строку "like". Я построил его в первую очередь с помощью SQL Profiler для анализа запроса, а затем скопировал биты, которые были настолько уникальными, насколько это возможно, для других возможных выполнений запросов. Может ли быть неправильная идентификация? Да. Если вы запускаете select миллион раз в секунду, это, вероятно, не является жизнеспособным решением. и т.п.

Но это работает для меня.

Ядро EF, выполняет свой запрос в хранимой процедуре. Но не работает с включениями, если вы используете хранимые процедуры. Мой мозг истекает кровью от попыток обернуть мою голову вокруг этого. Так что это болезненное решение, которое я использую.

/****** Object:  StoredProcedure [dbo].[WipePlanForDraftLoad]    Script Date: 11/01/2019 11:50:03 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:  Morten Bork, Henrik Davidsen    
-- Create date: 2019-01-11
-- Description: Procedure to Wipe a cached plan for a query that sort of matches a specific string
-- =============================================
CREATE PROCEDURE [dbo].[WipePlanForDraftLoad]
    -- Add the parameters for the stored procedure here

AS
BEGIN

declare plans cursor 
for 
select t1.plan_handle from sys.dm_exec_cached_plans t1
left outer join sys.dm_exec_query_stats t2 on t1.plan_handle = t2.plan_handle
outer apply sys.dm_exec_sql_text(sql_Handle) 
where text like '%(@__customerCode_0 bigint,@__from_1 datetime2(7),@__to_2 datetime2(7),@__currencyCode_3 varchar(255))%WHERE (\[c\].\[currency_code\] = @__currencyCode_3) AND (\[b\].\[Id\] = \[c\].\[booking_line_id\])) AND (\[booking\].\[Id\] = \[b\].\[booking_id\])%' escape '\'

declare @plan_handle varbinary(64)
open plans
fetch next from plans into @plan_handle
while @@FETCH_STATUS = 0
begin
    dbcc freeproccache(@plan_handle)
    fetch next from plans into @plan_handle
end
close plans
deallocate plans
END
GO
  • 1
    Спасибо за возвращение и обогащение этого сайта с таким подробным объяснением. Я часто вижу проблему с прослушиванием параметров, и обычно это происходит на sommarskog.se/query-plan-mysteries.html.
0

Некоторое время назад у меня была похожая проблема, и она имела отношение к определенным настройкам по умолчанию в SSMS, которые EF не устанавливает автоматически. Если вы можете, попробуйте установить ARITHABORT ON для сеанса EF из кода и посмотреть, выполняется ли запрос так же, как в SSMS.

  • 0
    Я выполнил context.Database.ExecuteSqlCommand ("SET ARITHABORT ON"); Прямо перед выполнением запроса. Это не помогло.
  • 0
    Это может не сработать, потому что каждое выполнение в рамках сущности находится в своей области видимости. Это необходимо сделать в контексте выполненного запроса (если вы передаете строку) или установить во время инициализации класса базы данных. Возможно, в качестве теста попробуйте передать сгенерированную строку SQL, но добавьте над ней оператор arithabort и посмотрите, будет ли это что-нибудь делать?
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню