如何使这个 SQL 函数更快?
Posted
技术标签:
【中文标题】如何使这个 SQL 函数更快?【英文标题】:How can I make this SQL function faster? 【发布时间】:2011-10-07 15:21:41 【问题描述】:背景
我有以下函数,它根据结果与搜索值的匹配程度对结果进行排名:
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
【问题讨论】:
【参考方案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
(我删除了您的第二个条件,因为它似乎没有必要,因为它返回与备用值相同的值。如果这是一个错误,那么如何修改上面的表达式以获得您想要的应该是相当明显的。)
然后像这样使用它:
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】:我猜你的函数也可以这样重写:
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
【讨论】:
【参考方案3】:我不确定您将如何优化此功能。
这里的主要问题是%somethings%
无处不在。
一位前同事对我的描述最好,因为您可以很容易地在电话簿中找到所有以 E 开头的名字,但是找到其中包含 E 的每个名字是一个非常长期运行的过程。
您实际上是在否定您的索引。
【讨论】:
以上是关于如何使这个 SQL 函数更快?的主要内容,如果未能解决你的问题,请参考以下文章