问题描述
现在,我正尝试提取某个域的所有登录页面,并按与某个组匹配的第一个UrlFilter的FilterType对其进行排序。到目前为止,这是我提出的LINQ:
var baseQuery = DbSet.AsNoTracking()
.Where(e => EF.Functions.Contains(EF.Property<string>(e,"Url"),$"\"{searchTerm}*\""))
.Where(e => e.DomainLandingPages.Select(lp => lp.DomainId).Contains(domainId));
var count = baseQuery.Count();
var page = baseQuery
.Select(e => new
{
LandingPage = e,UrlFilter = e.LandingPageUrlFilters.FirstOrDefault(f => f.UrlFilter.GroupId == groupId)
})
.Select(e => new
{
e.LandingPage,FilterType = e.UrlFilter == null ? UrlFilterType.NotCovered : e.UrlFilter.UrlFilter.UrlFilterType
})
.OrderBy(e => e.FilterType)
.Skip(10).Take(75).ToList();
现在,尽管这在技术上是可行的,但是它的执行速度非常慢,执行时间介于10到30秒之间,这对于用例来说还不够好。 LINQ转换为以下sql:
SELECT [l1].[Id],[l1].[LastUpdated],[l1].[Url],CASE
WHEN (
SELECT TOP(1) [l].[LandingPageId]
FROM [LandingPageUrlFilters] AS [l]
INNER JOIN [UrlFilters] AS [u] ON [l].[UrlFilterId] = [u].[Id]
WHERE ([l1].[Id] = [l].[LandingPageId]) AND ([u].[GroupId] = @__groupId_3)) IS NULL THEN 4
ELSE (
SELECT TOP(1) [u0].[UrlFilterType]
FROM [LandingPageUrlFilters] AS [l0]
INNER JOIN [UrlFilters] AS [u0] ON [l0].[UrlFilterId] = [u0].[Id]
WHERE ([l1].[Id] = [l0].[LandingPageId]) AND ([u0].[GroupId] = @__groupId_3))
END AS [FilterType]
FROM [LandingPages] AS [l1]
WHERE CONTAINS([l1].[Url],@__Format_1) AND @__domainId_2 IN (
SELECT [d].[DomainId]
FROM [DomainLandingPages] AS [d]
WHERE [l1].[Id] = [d].[LandingPageId]
)
ORDER BY CASE
WHEN (
SELECT TOP(1) [l2].[LandingPageId]
FROM [LandingPageUrlFilters] AS [l2]
INNER JOIN [UrlFilters] AS [u1] ON [l2].[UrlFilterId] = [u1].[Id]
WHERE ([l1].[Id] = [l2].[LandingPageId]) AND ([u1].[GroupId] = @__groupId_3)) IS NULL THEN 4
ELSE (
SELECT TOP(1) [u2].[UrlFilterType]
FROM [LandingPageUrlFilters] AS [l3]
INNER JOIN [UrlFilters] AS [u2] ON [l3].[UrlFilterId] = [u2].[Id]
WHERE ([l1].[Id] = [l3].[LandingPageId]) AND ([u2].[GroupId] = @__groupId_3))
END
OFFSET @__p_4 ROWS FETCH NEXT @__p_5 ROWS ONLY
现在我的问题是,我该如何缩短执行时间?通过sql或LINQ
with matched_urls as (
select l.id,min(f.urlfiltertype) as Filter
from landingpages l
join landingpageurlfilters lpf on lpf.landingpageid = l.id
join urlfilters f on lpf.urlfilterid = f.id
where f.groupid = @groupId
and contains(Url,'"barz*"')
group by l.id
) select l.id,5 as Filter
from landingpages l
where @domainId in (
select domainid
from domainlandingpages dlp
where l.id = dlp.landingpageid
) and l.id not in (select id from matched_urls ) and contains(Url,'"barz*"')
union select * from matched_urls
order by Filter
offset 10 rows fetch next 30 rows only
这执行起来还可以,将执行时间减少到〜5秒。由于这将用于表搜索,因此我希望进一步了解它。有什么方法可以改善此sql?
解决方法
您应该看一下生成的SQL。通常,我建议您学习SQL,编写执行性能良好的SQL查询并逐步解决(使用存储过程或原始SQL,或使用相同的原理设计LINQ查询。
我怀疑这会更好(未经测试):
var page = (
from e in baseQuery
let urlFilter = e.LandingPageUrlFilters.OrderBy(f => f.UrlFilterType).FirstOrDefault(f => f.UrlFilter.GroupId == groupId)
let filterType = urlFilter == null ? UrlFilterType.NotCovered : e.UrlFilter.UrlFilter.UrlFilterType
select new
{
LandingPage = e,FilterType = filterType
}
).Skip(10).Take(75).ToList();
,
缩短执行时间的一种方法是查看SSMS(SQL Server Management Studio)中的执行计划。
查看执行计划后,您可以设计一些索引,或者如果您没有相关经验,则可以查看SSMS是否建议一些索引。
接下来尝试创建索引并再次执行查询,看看执行时间是否得到了改善。
注意:这只是缩短执行时间的众多可能方式之一...