如何让这个SQL函数更快?

时间:2011-10-07 15:21:41

标签: c# sql sql-server nhibernate query-optimization

背景

我有以下功能,它根据搜索到的值与搜索值的匹配程度对结果进行排名:

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

3 个答案:

答案 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%

一位前同事对我说得最好,因为你可以很容易地在电话簿中找到以E开头的所有名字,但是在其中查找带有E的每个名字都是非常长时间运行的过程。< / p>

你实际上是在否定你的索引。