2011-11-29 130 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上進行分組,因此無需在該列上調用不同的分區(這可能會也可能不會提高性能,具體取決於系統選擇執行的查詢計劃)。