背景
我有以下功能,它根据搜索到的值与搜索值的匹配程度对结果进行排名:
CREATE FUNCTION [dbo].[fnGetRelevance]
(
@fieldName nvarchar(50),
@searchTerm nvarchar(50)
)
RETURNS int
AS
BEGIN
if (@fieldName like @searchTerm + '%') -- starts with
begin
return 0
end
else if ((@fieldName like '%' + @searchTerm + '%') and (@fieldName not like @searchTerm + '%')) -- contains, but doesn't start with
begin
return 1
end
return 1
END
因此,在以下查询(NHibernate查询)的上下文中,它根据搜索字符串Allianz RCM BRIC Stars A
搜索共享类,它会找到39个结果并对它们进行排名,以便完全匹配该字符串的那个顶部,其余部分按字母顺序排在其下方。
select top 50 *
from ShareManager.ShareClass sc
-- and a few other tables with an inner join and a left join
where (sc.ShareClass_Id in
(
/* filter by some business criteria which is a single
select statement that does 2 more inner joins */
)
and 1 = 1
and (sc.ShareClass_Name like '%Allianz%' /* @p11 */)
and (sc.ShareClass_Name like '%RCM%' /* @p12 */)
and (sc.ShareClass_Name like '%BRIC%' /* @p13 */)
and (sc.ShareClass_Name like '%Stars%' /* @p14 */)
and (sc.ShareClass_Name like '%A%' /* @p15 */)
order by dbo.fngetrelevance(sc.ShareClass_Name, 'Allianz RCM BRIC Stars A'), sc.ShareClass_Name asc
问题
我遇到的问题是dbo.fngetrelevance
导致我的NHibernate查询超时。我已经尝试延长超时但这不起作用,我认为这无论如何都不是问题。当我删除该功能时,它按预期工作。
SQL Server 2008上是否有办法加快速度,或者以不会超时的方式实现NHibernate的排名?
补充资料
我希望有人可能会建议我减少连接数量。我们已经进行了大量优化,以尽可能加快这些查询速度。在修改整体架构的规模上,我们要想出如何进一步优化,这将是一项巨大的努力。不幸的是,我们不会在游戏的这个阶段获得批准(并且目前我只能看到1个基金)
为了记录,这就是我在NHibernate中使用该函数的方式:
string querystring =
"select sc, sctr" +
" from ShareClass as sc" +
// joins to 2 other tables
" and (" + expressionTokenizer.ToResult("sc.Name") + ") "
+ this.AddShareClassOrder(order, "sc", "sctr", searchExpression);
var result = _session.CreateQuery(querystring)
.AddNameSearchCriteria(expressionTokenizer)
.AddDataUniverseParameters(dataUniverseHelper)
.SetFirstResult((pageSize * (pageNum - 1)))
.SetMaxResults(pageSize)
.List();
AddShareClassOrder
有效返回
fieldName = string.Format("dbo.fngetrelevance({1}.{2}, '{0}'), {1}.{2}", textToSearchFor, shareClassPrefix, "Name");
return String.Format(" order by {0} {1}", fieldName, direction);
或者,以下是在SQL中表示的:
dbo.fngetrelevance(sc.ShareClass_Name, 'Allianz RCM BRIC Stars A'), sc.ShareClass_Name asc
答案 0 :(得分:1)
我猜你的功能可以像这样重写:
CREATE FUNCTION [dbo].[fnGetRelevance]
(
@fieldName nvarchar(50),
@searchTerm nvarchar(50)
)
RETURNS int
AS
BEGIN
if (@fieldName like @searchTerm + '%') -- starts with
begin
return 0
end
return 1
END
因为只有当@fieldName以@searchTerm开头时才返回0,而在所有其他情况下则返回1。
而不是调用函数
order by dbo.fngetrelevance(sc.ShareClass_Name, 'Allianz RCM BRIC Stars A'), sc.ShareClass_Name asc
您可以使用以下内容:
order by
case when sc.ShareClass_Name like 'Allianz RCM BRIC Stars A%'
then 0 else 1 end,
sc.ShareClass_Name asc
答案 1 :(得分:1)
我必须不同意这里发布的其他答案; %%
这里使用的是一个红色的鲱鱼,因为你没有对这个表达式进行任何过滤(在这种情况下,它们肯定是正确的)。你的问题是你的UDF;就像现在一样,你的UDF将不内联到查询中。相反,查询引擎将获取整个结果集,为每一行调用该函数,然后捕获这些结果以进行排序。您需要定义和使用您的函数,以便将其内联到查询中。
有关详细信息,请参阅this article,但简短版本是将您的功能更改为:
CREATE FUNCTION [dbo].[fnGetRelevance]
(
@fieldName nvarchar(50),
@searchTerm nvarchar(50)
)
RETURNS table
AS
select (case when @fieldName like @searchTerm + '%' then 0 else 1 end) as Value
(我删除了你的第二个条件,因为它似乎没有必要,因为它返回了与回退值相同的值。如果这是一个错误,那么如何修改上面的表达式以获得你想要的东西应该是相当明显的。)< / p>
然后像这样使用它:
select top 50 *
from ShareManager.ShareClass sc
-- and a few other tables with an inner join and a left join
where (sc.ShareClass_Id in
(
/* filter by some business criteria which is a single
select statement that does 2 more inner joins */
)
and 1 = 1
and (sc.ShareClass_Name like '%Allianz%' /* @p11 */)
and (sc.ShareClass_Name like '%RCM%' /* @p12 */)
and (sc.ShareClass_Name like '%BRIC%' /* @p13 */)
and (sc.ShareClass_Name like '%Stars%' /* @p14 */)
and (sc.ShareClass_Name like '%A%' /* @p15 */)
order by (select Value from dbo.fngetrelevance(sc.ShareClass_Name, 'Allianz RCM BRIC Stars A')), sc.ShareClass_Name asc
我无法谈论如何更改您的NHibernate查询来执行此操作,但这不是您的问题。
答案 2 :(得分:0)
我不确定你能够多好地优化这个功能。
这里的主要问题是到处都是%somethings%
。
你实际上是在否定你的索引。