#sql-server #tsql #optimization #indexing #user-defined-functions
#sql-сервер #tsql #оптимизация #индексирование #определяемые пользователем функции
Вопрос:
У меня есть хранимая процедура, которая принимает строку и сравнивает ее с несколькими полями. Однако одно из полей содержит дополнительные символы, которые необходимо удалить, прежде чем сравнивать его со строкой поиска.
например
Create Or Alter Proc FindPosts3
(
@criteria AS nvarchar(25)
)
AS
Begin
Select P.OwnerUserId,P.CreationDate,p.Score,p.CommentCount,p.Body1
From Posts1 p Inner Join
(
Select p.OwnerUserId,max(p.CreationDate) as CreationDate
From Posts1 p
Group by p.OwnerUserId
) Sub On P.OwnerUserId = Sub.OwnerUserId AND P.CreationDate = Sub.CreationDate
Where p.Score = @criteria OR p.CommentCount = @criteria Or udfstripHtmlTags(p.body1) = @criteria
End
Примерные значения полей:
OwernerUserID---CreationDate----Score----CommentCount----Body
1 Aug 20, 2010 18 6 <p>null<p>
2 Dec 15, 2008 7 3 <b>variable<b>
3 Mar 07, 2011 15 20 <i>Arrays<i>
При поиске в поле body <p>, <b> and <i>
теги удаляются с помощью функции udfStripHtmlTag . Это приводит к тому, что поле Body в предложении where больше не является SARG
аргументом поиска » или», что замедляет выполнение запроса.
В этом примере я удаляю HTML-теги из поля body, используя функцию с именем ‘udfStripHtmlTag’, прежде чем сравнивать ее с критериями. Как и ожидалось, это приводит к тому, что вместо поиска по индексу выполняется сканирование индекса с индексом, созданным для поля body . Фактически, это также приводит к тому, что индексы для оценки и commentcount
полей больше не используются в этой версии запроса. В другой версии, где я использую объединение вместо «или», индексы для оценки и commentcount
полей по-прежнему используются. Однако запрос по-прежнему выполняется так же медленно из-за сканирования индекса для основного поля.
Удаление символов из поля является обязательным требованием. Есть ли альтернативный метод, который я могу использовать вместо скалярной функции в предложении where?
примечание. Это не мой исходный код проблемы. Скорее это код, основанный на базе данных StackOverflow. Мой исходный код проблемы основан на большом количестве конфиденциальных данных / полей, поэтому мне было проще использовать альтернативную базу данных. Аналогично, поле body в базе данных StackOverflow было изменено на nvarchar(30)
, чтобы я мог создать для него индекс. Наконец, запрос выполняется менее чем за секунду без скалярной функции.
Комментарии:
1. Функции с табличным значением, как правило, более производительны, но примеры данных и желаемых результатов были бы полезны.
2. Удаление символов из поля является обязательным требованием. Это источник вашей проблемы. если вам нужно выполнить поиск и вам нужно удалить из него html для поиска, то сделайте это ОДИН РАЗ при вставке или обновлении этого столбца.
3. Это не мой исходный код проблемы , тогда любые предложения, которые вы можете получить, могут быть полностью неосновными.
4. @SMor, хотя это не моя первоначальная проблема, я смог воспроизвести те же проблемы с производительностью. Проблема заключается в функции в предложении where . Если я смогу решить это здесь, это также решит мою первоначальную проблему. Кроме того, ваше предложение правильно делать это при обновлении или вставке. Однако я не могу изменить исходную базу данных. Я пишу запросы только для извлечения данных.
5. @JohnCappelletti Я добавил таблицу, я не знаю, полезно ли это. Тем не менее, результаты, которые я получаю, в порядке. Просто функция приводит к тому, что индекс не используется, что приводит к тому, что выполнение запроса занимает около полутора минут.
Ответ №1:
Не уверен, что это более эффективно, чем ваша скалярная функция
Устав извлекать строку, я модифицировал функцию синтаксического анализа, чтобы принимать два разных разделителя.
Пример
Declare @YourTable Table ([OwernerUserID] int,[CreationDate] varchar(50),[Score] int,[CommentCount] int,[Body] varchar(150))
Insert Into @YourTable Values
(1,'Aug 20, 2010',18,6,'<p>null</p>')
,(2,'Dec 15, 2008',7,3,'<b>variable</b>')
,(3,'Mar 07, 2011',15,20,'<i>Arrays</i>')
Select A.*
From @YourTable A
Cross Apply [dbo].[tvf-Str-Extract](Body,'>','</') B
Where B.RetVal like 'variable%'
ВОЗВРАТ
OwernerUserID CreationDate Score CommentCount Body
2 Dec 15, 2008 7 3 <b>variable</b>
Табличная функция, если она заинтересована
CREATE FUNCTION [dbo].[tvf-Str-Extract] (@String varchar(max),@Delim1 varchar(100),@Delim2 varchar(100))
Returns Table
As
Return (
Select RetSeq = row_number() over (order by RetSeq)
,RetVal = left(RetVal,charindex(@Delim2,RetVal)-1)
From (
Select RetSeq = row_number() over (order by 1/0)
,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From ( values (convert(xml,'<x>' replace((Select replace(@String,@Delim1,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>') '</x>').query('.'))) as A(XMLData)
Cross Apply XMLData.nodes('x') AS B(i)
) C1
Where charindex(@Delim2,RetVal)>1
)
/*
Max Length of String 1MM characters
Declare @String varchar(max) = 'Dear [[FirstName]] [[LastName]], ...'
Select * From [dbo].[tvf-Str-Extract] (@String,'[[',']]')
*/
Комментарии:
1. Большое вам спасибо. Это именно то, что мне нужно. Теперь мой запрос выполняется менее чем за секунду при поиске более 4 миллионов записей. Странно, что многие онлайн-ресурсы советуют не использовать скалярные функции в предложениях where, но не предлагают жизнеспособной альтернативы. Для меня это решение должно работать в самых разных сценариях. Еще раз спасибо.
2. Чтобы было ясно, логика в вашей табличной функции не имела значения. Я создал tvf со своей собственной логикой, а затем использовал перекрестное применение. Затем я сравнил возвращаемое значение из cross apply с моими критериями так же, как и в примере.