Как я могу сделать эту функцию SQL быстрее? - PullRequest
0 голосов
/ 07 октября 2011

Фон

У меня есть следующая функция, которая ранжирует результаты по тому, насколько близко они соответствуют искомому значению:

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 результатов и ранжирует их так, чтобы тот, который точно совпадает с этой строкой, был сверху, а остальные отсортированы в алфавитном порядке нижеit.

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 ]

1 голос
/ 07 октября 2011

Полагаю, ваша функция может быть переписана так:

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

потому что вы возвращаете 0 только тогда, когда @fieldName начинается с @searchTerm и 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 голос
/ 07 октября 2011

Я должен не согласиться с другими ответами, опубликованными здесь; %% здесь использует красную сельдь, поскольку вы не выполняете фильтрацию этого выражения (в этом случае они наверняка будут правы). Ваша проблема - ваш UDF; в нынешнем виде ваш UDF будет не встроен в запрос. Вместо этого механизм запросов примет весь набор результатов, вызовет функцию для каждой отдельной строки и затем захватит эти результаты для сортировки. Вам необходимо определить и использовать вашу функцию таким образом, чтобы она была встроена в запрос.

Для получения дополнительной информации см. эту статью , но краткая версия должна изменить вашу функцию следующим образом:

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, но это не ваша проблема.

0 голосов
/ 07 октября 2011

Я не уверен, насколько хорошо вы сможете оптимизировать эту функцию.

Основная проблема здесь такая же, как %somethings% везде.

бывший коллега описал это лучше всего для меня, поскольку вы можете легко найти все имена, начинающиеся с буквы E, в телефонной книге, но поиск каждого имени с буквой E - это очень длительный процесс.

Вы фактически отрицаете свою индексацию.

...