"План запросов LINQ" ужасно неэффективен, но "Query Analyzer query plan" идеально подходит для того же SQL!

У меня есть запрос LINQ to SQL, который генерирует следующий SQL:

exec sp_executesql N'SELECT COUNT(*) AS [value]
FROM [dbo].[SessionVisit] AS [t0]
WHERE ([t0].[VisitedStore] = @p0) AND (NOT ([t0].[Bot] = 1)) AND 
([t0].[SessionDate] > @p1)',N'@p0 int,@p1 datetime',
@p0=1,@p1='2010-02-15 01:24:00'

(Это фактический SQL, взятый из SQL Profiler на SQL Server 2008.)

План запроса, сгенерированный при запуске SQL из Query Analyser, идеален. Он использует индекс, содержащий VisitedStore, Bot, SessionDate. Запрос возвращается мгновенно.

Однако, когда я запускаю это из С# (с LINQ), используется другой план запроса, который настолько неэффективен, что даже не возвращается через 60 секунд. Этот план запроса пытается выполнить ключевой поиск кластерного первичного ключа, который содержит пару миллионов строк. У него нет шансов вернуться.

То, что я просто не могу понять, это то, что выполняется ТОЧНЫЙ тот же SQL - либо из LINQ, либо из Query Analyzer, но план запроса отличается.

Я запускал два запроса много раз, и теперь они работают изолированно от любых других запросов. Дата DateTime.Now.AddDays(-7), но я даже жестко запрограммировал эту дату для устранения проблем с кешированием.

Есть ли что-нибудь, что я могу изменить в LINQ to SQL, чтобы повлиять на план запроса или попытаться отладить это дальше? Я очень смущен!

Ответ 1

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

SELECT 
    object_name = Object_Name(ind.object_id),
    IndexName = ind.name,
    StatisticsDate = STATS_DATE(ind.object_id, ind.index_id)
FROM SYS.INDEXES ind
order by STATS_DATE(ind.object_id, ind.index_id) desc

Статистика должна обновляться в недельном плане обслуживания. Для быстрого исправления выполните следующую команду для обновления всей статистики в вашей базе данных:

exec sp_updatestats

Помимо статистики, еще одна вещь, которую вы можете проверить, это Параметры SET. Они могут отличаться от Query Analyzer и вашего приложения Linq2Sql.

Другая возможность заключается в том, что SQL Server использует старый кешированный план для вашего запроса Linq2Sql. Планы можно кэшировать для каждого пользователя, поэтому, если вы запустите Query Analyzer как другой пользователь, который может объяснить разные планы. Обычно вы можете добавить Option (RECOMPILE) к запросу приложения, но я думаю, что это сложно с Linq2Sql. Вы можете очистить весь кеш с помощью DBCC FREEPROCCACHE и посмотреть, ускоряет ли этот запрос Linq2Sql.

Ответ 2

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

надеюсь, это, по крайней мере, поможет кому-либо в той же лодке, что и я