Мне был задан следующий запрос в SQL (имена переменных obfuscated), который пытается получить значения (Ch, Wa, Bu, Hi), что приводит к наибольшему числу (cnt) записей Pi.
select top 1 Pi.Ch, Pi.Wa, Pi.Bu, Pi.Hi, COUNT(1) as cnt
from Product, Si, Pi
where Product.Id = Si.ProductId
and Si.Id = Pi.SiId
and Product.Code = @CodeParameter
group by Pi.Ch, Pi.Wa, Pi.Bu, Pi.Hi
order by cnt desc
который запускается мгновенно в студии управления SQL в нашей производственной базе данных. Я успешно написал код несколькими способами в С# LINQ и Entity Framework, но каждый раз код работает через 8-10 секунд. Одной из попыток является следующий код (без него, поскольку один вызов дает одинаковые результаты):
using(var context = new MyEntities()){
var query = context.Products
.Where(p => p.Code == codeFromFunctionArgument)
.Join(context.Sis, p => p.Id, s => s.ProductId, (p, s) => new { sId = s.Id })
.Join(context.Pis, ps => ps.sId, pi => pi.SiId, (ps, pi) => new {pi.Ch, pic.Wa, pic.Bu, pic.Hi})
.GroupBy(
pi => pi,
(k, g) => new MostPisResult()
{
Ch = k.Ch,
Wa = k.Wa,
Bu = k.Bu,
Hi = k.Hi,
Count = g.Count()
}
)
.OrderByDescending(x => x.Count);
Console.WriteLine(query.ToString());
return query.First();
}
}
который выводит следующие операторы SQL:
SELECT
[Project1].[C2] AS [C1],
[Project1].[Ch] AS [Ch],
[Project1].[Wa] AS [Wa],
[Project1].[Bu] AS [Bu],
[Project1].[Hi] AS [Hi],
[Project1].[C1] AS [C2]
FROM ( SELECT
[GroupBy1].[A1] AS [C1],
[GroupBy1].[K1] AS [Ch],
[GroupBy1].[K2] AS [Wa],
[GroupBy1].[K3] AS [Bu],
[GroupBy1].[K4] AS [Hi],
1 AS [C2]
FROM ( SELECT
[Extent3].[Ch] AS [K1],
[Extent3].[Wa] AS [K2],
[Extent3].[Bu] AS [K3],
[Extent3].[Hi] AS [K4],
COUNT(1) AS [A1]
FROM [dbo].[Product] AS [Extent1]
INNER JOIN [dbo].[Si] AS [Extent2] ON [Extent1].[Id] = [Extent2].[ProductId]
INNER JOIN [dbo].[Pi] AS [Extent3] ON [Extent2].[Id] = [Extent3].[SiId]
WHERE ([Extent1].[Code] = @p__linq__0) AND (@p__linq__0 IS NOT NULL)
GROUP BY [Extent3].[Ch], [Extent3].[Wa], [Extent3].[Bu], [Extent3].[Hi]
) AS [GroupBy1]
) AS [Project1]
ORDER BY [Project1].[C1] DESC
Я также пробовал в синтаксисе запроса примерно с одним и тем же результатом. Я также пытался (но не очень долго) выполнить исходный запрос SQL непосредственно с EF, но не смог быстро заставить его работать.
Есть ли какая-то ошибка, которую я делаю при переводе запроса в LINQ? Есть ли очевидный способ, который я пропускаю, чтобы улучшить запрос? Можно ли написать запрос в EF/LINQ с той же производительностью, что и операторы SQL?
====== Обновление ======
В профилировщике SQL вывод для исходного запроса точно такой же. Для запроса LINQ это очень похоже на то, что я опубликовал выше.
exec sp_executesql N'SELECT TOP (1)
[Project1].[C2] AS [C1],
[Project1].[Ch] AS [Ch],
[Project1].[Wa] AS [Wa],
[Project1].[Bu] AS [Bu],
[Project1].[Hi] AS [Hi],
[Project1].[C1] AS [C2]
FROM ( SELECT
[GroupBy1].[A1] AS [C1],
[GroupBy1].[K1] AS [Ch],
[GroupBy1].[K2] AS [Wa],
[GroupBy1].[K3] AS [Bu],
[GroupBy1].[K4] AS [Hi],
1 AS [C2]
FROM ( SELECT
[Extent3].[Ch] AS [K1],
[Extent3].[Wa] AS [K2],
[Extent3].[Bu] AS [K3],
[Extent3].[Hi] AS [K4],
COUNT(1) AS [A1]
FROM [dbo].[Product] AS [Extent1]
INNER JOIN [dbo].[Si] AS [Extent2] ON [Extent1].[Id] = [Extent2].[ProductId]
INNER JOIN [dbo].[Pi] AS [Extent3] ON [Extent2].[Id] = [Extent3].[SiId]
WHERE ([Extent1].[Code] = @p__linq__0) AND (@p__linq__0 IS NOT NULL)
GROUP BY [Extent3].[Ch], [Extent3].[Wa], [Extent3].[Bu], [Extent3].[Hi]
) AS [GroupBy1]
) AS [Project1]
ORDER BY [Project1].[C1] DESC',N'@p__linq__0 nvarchar(4000)',@p__linq__0=N'109579'
====== Обновление 2 ======
Здесь запутанный XML-вывод плана выполнения запроса на Snipt.org. Обратите внимание, что данная переменная здесь называется "MagicalCode" на выходе, и оба значения "109579" и "2449-268-550" действительны (строки в С#), как в последней строке выходного файла XML.
<ParameterList>
<ColumnReference
Column="@p__linq__0"
ParameterCompiledValue="N'109579'"
ParameterRuntimeValue="N'2449-268-550'" />
</ParameterList>
Отобразить изображение с фактическими показателями строк
====== Обновление 3 ======
(скрыто в комментарии) Я запустил SQL файл, созданный EF, из сущности в SSMS, и он запускался мгновенно. Таким образом, я мог бы страдать от какой-то формы нюхания параметров, как это намекал этот вопрос. Я не уверен, как справиться с этим в контексте структуры сущностей.
====== Обновление 4 ======
Обновлен План выполнения SQL Entity Framework и Выполнение SQL запросов SQL SSMS Plan, который можно открыть с помощью Plan Explorer.
====== Обновление 5 ======
Некоторые попытки обхода пути
- Запуск исходного запроса с помощью
context.Database.SqlQuery<ReturnObject>(...)
выполнялся через ~ 4-5 секунд. - Запуск исходного запроса с помощью
SqlCommand
и строка подключения, полученная из контекста EF, заняла около 3 секунд (служебные данные инициализации контекста). - Выполнение исходного запроса с использованием
SqlCommand
, взятого с жестко привязанной строкой, занимает около 1,5 секунд. Таким образом, я в конечном итоге использовал последний. Последнее, о чем я могу думать, это написать хранимую процедуру, чтобы приблизиться к "мгновенной" производительности запуска запроса в SSMS.