Альтернативы для генерации запроса к серверу sql из лямбда-выражения - PullRequest
2 голосов
/ 29 ноября 2011

Я написал лямбда-выражение, которое дает ожидаемые результаты, однако оно генерирует абсолютно массивный SQL-запрос и имеет плохую производительность. Внизу смотрите статистику по времени / времени.

Есть ли другой способ выполнить приведенный ниже запрос?

select distinct(searchterms) as SearchTerms, max(totalresults) FROM cmsSearchLog 
where totalresults != 0 and searchterms like 'de%' group by searchterms 
order by max(totalresults) desc

Фрагменты кода C #:

// current lamda expression; has bad performance compared to above query
List<SearchTerm> existingSearchTerms1 = context.cmsSearchLogs.Where(oq =>
context.cmsSearchLogs.Where(q =>
q.SearchTerms.ToLower().Contains(terms.ToLower()) && q.TotalResults != 0)
.Select(s => s.SearchTerms)
.Distinct()
.Contains(oq.SearchTerms))
.Select(a => new { a.SearchTerms, a.TotalResults })
.GroupBy(gb => gb.SearchTerms)
.OrderByDescending(ob => ob.Max(m => m.TotalResults))
.Select(s => new SearchTerm()
    {
        SearchTerms = s.FirstOrDefault().SearchTerms,
        TotalResults = s.FirstOrDefault().TotalResults
    }
)
.ToList();

// get the suggestions back as a list of strings
List<string> suggestions = Enumerable.Range(0, 
  existingSearchTerms1.Count())
  .Select(x => existingSearchTerms1.ElementAt(x).SearchTerms).ToList();

Это закрытый класс для хранения результатов запроса

private class SearchTerm
{
    public string SearchTerms { get; set; }
    public int TotalResults { get; set; }
}

SQL, сгенерированный лямбда-выражением, огромен:

SELECT 
[Project13].[C2] AS [C1], 
[Project13].[C3] AS [C2], 
[Project13].[C4] AS [C3]
FROM ( SELECT 
    [Project12].[C1] AS [C1], 
    1 AS [C2], 
    [Project12].[C2] AS [C3], 
    [Project12].[C3] AS [C4]
    FROM ( SELECT 
        [Project8].[C1] AS [C1], 
        [Project8].[C2] AS [C2], 
        (SELECT TOP (1) 
            [Extent5].[TotalResults] AS [TotalResults]
            FROM [dbo].[cmsSearchLog] AS [Extent5]
            WHERE ( EXISTS (SELECT 1 AS [C1]                    
               FROM ( SELECT DISTINCT 
            [Extent6].[SearchTerms] AS [SearchTerms]
            FROM [dbo].[cmsSearchLog] AS [Extent6]
            WHERE (( CAST(CHARINDEX(LOWER('dew'), 
                             LOWER([Extent6].[SearchTerms])) AS int)) > 0) 
                             AND (0 <> [Extent6].[TotalResults])
                )  AS [Distinct3]
            WHERE [Distinct3].[SearchTerms] = [Extent5].[SearchTerms]
            )) AND ([Project8].[SearchTerms] = [Extent5].[SearchTerms])) 
                                AS [C3]
        FROM ( SELECT 
           [Project7].[C1] AS [C1], 
           [Project7].[SearchTerms] AS [SearchTerms], 
           [Project7].[C2] AS [C2]
           FROM ( SELECT 
              [Project3].[C1] AS [C1], 
              [Project3].[SearchTerms] AS [SearchTerms], 
              (SELECT TOP (1) 
              [Extent3].[SearchTerms] AS [SearchTerms]
              FROM [dbo].[cmsSearchLog] AS [Extent3]
              WHERE ( EXISTS (SELECT 1 AS [C1] FROM ( SELECT DISTINCT 
            [Extent4].[SearchTerms] AS [SearchTerms]
            FROM [dbo].[cmsSearchLog] AS [Extent4]
            WHERE (( CAST(CHARINDEX(LOWER('dew'), 
                             LOWER([Extent4].[SearchTerms])) AS int)) > 0) 
                             AND (0 <> [Extent4].[TotalResults]))  AS [Distinct2] 
           WHERE [Distinct2].[SearchTerms] = [Extent3].[SearchTerms]
               )) AND ([Project3].[SearchTerms] = [Extent3].[SearchTerms])) AS [C2]
                FROM ( SELECT 
                  [GroupBy1].[A1] AS [C1], 
                  [GroupBy1].[K1] AS [SearchTerms]
                  FROM ( SELECT 
                   [Extent1].[SearchTerms] AS [K1], 
                   MAX([Extent1].[TotalResults]) AS [A1]
                   FROM [dbo].[cmsSearchLog] AS [Extent1]
                   WHERE EXISTS (SELECT 1 AS [C1]
                FROM ( SELECT DISTINCT [Extent2].[SearchTerms]
                  AS [SearchTerms] FROM [dbo].[cmsSearchLog] AS [Extent2]
                        WHERE (( CAST(CHARINDEX(LOWER('dew'),
                                      LOWER([Extent2].[SearchTerms])) AS int)) > 0)
                                       AND (0 <> [Extent2].[TotalResults]))  AS [Distinct1]
                                       WHERE [Distinct1].[SearchTerms] = [Extent1].[SearchTerms])
                 GROUP BY [Extent1].[SearchTerms])  AS [GroupBy1]
                )  AS [Project3]
            )  AS [Project7]
        )  AS [Project8]
    )  AS [Project12]
)  AS [Project13]
ORDER BY [Project13].[C1] ASC

Я выполнил оба запроса с включенной статистикой времени и времени, и результаты приведены ниже. (примечание: сначала сгенерированный лямбда-запрос, потом мой рукописный запрос). Это подтверждает моё подозрение, что сгенерированный запрос выполняется ужасно по сравнению с запросом, который я действительно хочу.

(8 row(s) affected)
Table 'cmsSearchLog'. Scan count 6, logical reads 106, physical reads 0, 
read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 1 ms.

(7 row(s) affected)
Table 'cmsSearchLog'. Scan count 1, logical reads 5, physical reads 0, 
read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

Ответы [ 5 ]

6 голосов
/ 29 ноября 2011

Попробуйте этот запрос вместо текущего запроса LINQ:

var query = from x in context.cmsSearchLog
            where totalresults != 0 &&
                  searchterms.BeginsWith("de")
            group x by x.searchterms into terms
            select new {
                           SearchTerms = terms.Key(),
                           TotalResults = terms.Max(t => t.totalresults)
                       };

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

1 голос
/ 29 ноября 2011

Перевод LINQ (будь то LINQ to SQL, Entity Framework и т. Д.) - это эффективная разработка . Это позволяет (теоретически) создавать более читабельный, обслуживаемый код и снижает вероятность ошибок базы данных во время выполнения из-за переполнения и т. Д. LINQ не о производительности. LINQ обычно обеспечивает «достаточно хорошую» производительность, но никогда не выбьет что-то ближе к металлу, например, запрос с ручным кодированием или хранимую процедуру.

Тем не менее, ваши запросы возвращают различное количество строк, поэтому один (или оба) из них неправильный; первый запрос производит 8 строк, а второй - 7. Вы не можете очень хорошо сравнить запросы, которые дают разные результаты!

0 голосов
/ 29 ноября 2011

Почему бы просто не позволить вашей базе данных обрабатывать работу по этому запросу и выгружать результаты непосредственно в ваш класс SearchTerm?Вы можете параметризировать процедуру, если вам нужно искать конкретные термины.В приведенном вами примере вы можете еще больше повысить производительность, проиндексировав столбец searchterms, поскольку подстановочный знак в предложении where ссылается на завершающую часть текста значения столбца.Кроме того, поскольку вы группируете по поисковым запросам, нет необходимости вызывать различные для этого столбца (это может повысить или не повысить производительность в зависимости от того, какой план запросов выберет ваша система).

0 голосов
/ 29 ноября 2011

Прежде всего, вам нужно знать, что подход лямбда-выражений не подходит для такого рода запросов.Но, если вы в порядке с хаком, создайте представление, которое использует:

select distinct searchTerm, max(totalresults) 
from cmsSearchLog 
group by searchterms 
order by max(totalresults) desc

, а затем используйте свое лямбда-выражение для выполнения фильтрующей части

0 голосов
/ 29 ноября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...