从lambda表达式生成sql server查询的备选方案
本文关键字:查询 方案 server sql lambda 表达式 | 更新日期: 2023-09-27 18:15:56
我写了一个lambda表达式,它产生了预期的结果,但是它产生了一个绝对庞大的sql查询,它的性能很差。查看最底部的io/time统计信息。
是否有其他方法来实现下面的查询?
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
我在io和时间统计打开的情况下执行了两个查询,结果如下:(注意: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.
尝试用这个查询代替当前的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)
};
我还没有测试过,但我认为它会生成一个相当有效的查询并返回所需的结果。
LINQ转换(无论是LINQ到SQL,实体框架等)是关于高效开发的。它允许(在理论上)更具可读性和可维护性的代码,并且减少了由于fat-fingering等导致的运行时数据库错误的可能性。LINQ是关于性能的而不是。LINQ通常提供"足够好"的性能,但它永远无法击败一些更接近金属的东西,比如手工编码的查询或存储过程。
也就是说,您的查询返回不同的行计数,因此其中一个(或两个)是错误的;第一个查询产生8行,而第二个查询产生7行。您无法很好地比较提供不同结果的查询!
对于复杂或性能密集型查询,不要觉得不能创建视图或用户定义函数并映射到它们。在这种情况下,您甚至可以使用存储过程并映射到该过程。
为什么不让数据库处理此查询的工作并将结果直接转储到SearchTerm类中呢?如果需要查找特定的术语,可以对过程进行参数化。在您提供的示例中,您可以通过索引searchterms列进一步提高性能,因为where子句中的通配符引用列值文本的末尾部分。此外,由于是根据搜索词进行分组,因此不需要在该列上调用distinct(这可能会提高性能,也可能不会提高性能,这取决于系统选择执行的查询计划)。
首先,您需要知道lambda表达式方法不适用于这种查询。但是,如果您不介意这样做,可以创建一个使用:
的视图。select distinct searchTerm, max(totalresults)
from cmsSearchLog
group by searchterms
order by max(totalresults) desc
然后使用lambda表达式执行过滤部分