2011-11-29 92 views
2

我写了一个lambda表达式,它产生了预期的结果,但它生成了一个绝对巨大的sql查询,并且性能不佳。查看io /时间统计的底部。从lambda表达式生成sql server查询的替代方法

是否有另一种方法来实现下面的查询?

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; } 
} 
保存结果

由lambda表达式生成的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 

I执行两个查询与i​​o和时间统计打开,结果如下。 (注意:lambda生成的查询是第一个,我的手写查询第二)因此,这证实了我怀疑生成的查询执行可怕比我实际需要查询。

(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. 
+1

从来没有人声称linq to sql每次都会生成完美的sql。这看起来像使用手工优化的存储过程而不是lambda生成的调用的好地方。 – asawyer

回答

4

试试这个查询,而不是当前的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) 
         }; 

我没有测试过,但我相信它会产生一个非常高效的查询,并返回所期望的结果。

+0

完美,正是我所期待的!为了执行它,我必须在组之前移动where子句。它生成的SQL实际上与我手动编码的查询相同,并且其性能非常好。谢谢! – TugboatCaptain

+0

没问题,很高兴为你工作。我也会更新我的答案,并通过移动小组。 – shuniar

0

LINQ翻译(无论是LINQ to SQL中,实体框架等)约为高效发展。它允许(理论上)更易读,可维护的代码,以及由于胖指法等导致的运行时数据库错误的可能性降低等。关于性能,LINQ是而不是。 LINQ通常提供了“足够好”的性能,但它绝不会像手写代码查询或存储过程那样击败更接近金属的东西。

也就是说,您的查询返回不同的行数,所以它们中的一个(或两个)都是错误的;第一个查询生成8行,而第二个查询生成7.您不能很好地比较提供不同结果的查询!

+0

Downvoter谨慎解释? –

+0

简单。 Lambda对于多级选择,截然不同的等级来说是非常低效的 - 可怕的SQL完全是程序员的错误,而不是所讨论的技术。 – TomTom

0

对于复杂或性能密集的查询,不要觉得您不能创建视图或用户定义的函数并映射到该函数。在这种情况下,你甚至可以使用存储过程并映射到该过程。

0

首先,您需要知道lambda表达式方法不适用于这种查询。但是,如果你都OK了黑客,创建使用一个观点:

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

然后用你的lambda表达式做滤波部分

0

为什么不让你的数据库处理这个查询工作,将结果直接转储到您的SearchTerm类中?如果您需要查找特定术语,则可以参数化该过程。在您提供的示例中,您可以通过索引searchterms列来进一步提高性能,因为where子句中的通配符引用列值文本的尾部。此外,由于您在searchterms上进行分组,因此无需在该列上调用不同的分区(这可能会也可能不会提高性能,具体取决于系统选择执行的查询计划)。