在 Where 子句中放置标量函数的替代方法
Alternative to placing scalar function in Where clause
我有一个存储过程,它接受一个字符串并将其与多个字段进行比较。但是,其中一个字段包含在将其与搜索字符串进行比较之前需要删除的其他字符。
例如
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>
搜索正文字段时,使用 udfStripHtmlTag 函数删除了 <p>, <b> and <i>
标签。这会导致 where 子句中的正文字段不再是“SARG
”或减慢查询速度的搜索参数。
在这个例子中,我在将它与标准进行比较之前使用一个名为 'udfStripHtmlTag' 的函数从正文字段中删除 HTML 标签。正如预期的那样,这会导致发生索引扫描,而不是使用为正文字段创建的索引进行索引查找。事实上,这也会导致 score 和 commentcount
字段的索引不再用于此版本的查询。在我使用 union 而不是 'or' 的另一个版本中,分数和 commentcount
字段的索引仍然被使用。但是,由于 body 字段的索引扫描,查询仍然很慢。
从字段中剥离字符是一项要求。有没有我可以使用的替代技术来代替 where 子句中的标量函数?
注意。这不是我原来的问题代码。相反,它是基于 Whosebug 数据库的代码。我原来的问题代码是基于很多敏感的 data/fields 所以我更容易使用替代数据库。同样,Whosebug 数据库中的正文字段已更改为 nvarchar(30)
,因此我可以为其创建索引。最后,查询在没有标量函数的情况下运行不到一秒。
不确定这是否比您的标量函数更高效
厌倦了提取字符串,我修改了一个解析函数以接受两个不同的分隔符。
例子
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%'
Returns
OwernerUserID CreationDate Score CommentCount Body
2 Dec 15, 2008 7 3 <b>variable</b>
Table-值函数如果感兴趣
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,'[[',']]')
*/
我有一个存储过程,它接受一个字符串并将其与多个字段进行比较。但是,其中一个字段包含在将其与搜索字符串进行比较之前需要删除的其他字符。
例如
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>
搜索正文字段时,使用 udfStripHtmlTag 函数删除了 <p>, <b> and <i>
标签。这会导致 where 子句中的正文字段不再是“SARG
”或减慢查询速度的搜索参数。
在这个例子中,我在将它与标准进行比较之前使用一个名为 'udfStripHtmlTag' 的函数从正文字段中删除 HTML 标签。正如预期的那样,这会导致发生索引扫描,而不是使用为正文字段创建的索引进行索引查找。事实上,这也会导致 score 和 commentcount
字段的索引不再用于此版本的查询。在我使用 union 而不是 'or' 的另一个版本中,分数和 commentcount
字段的索引仍然被使用。但是,由于 body 字段的索引扫描,查询仍然很慢。
从字段中剥离字符是一项要求。有没有我可以使用的替代技术来代替 where 子句中的标量函数?
注意。这不是我原来的问题代码。相反,它是基于 Whosebug 数据库的代码。我原来的问题代码是基于很多敏感的 data/fields 所以我更容易使用替代数据库。同样,Whosebug 数据库中的正文字段已更改为 nvarchar(30)
,因此我可以为其创建索引。最后,查询在没有标量函数的情况下运行不到一秒。
不确定这是否比您的标量函数更高效
厌倦了提取字符串,我修改了一个解析函数以接受两个不同的分隔符。
例子
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%'
Returns
OwernerUserID CreationDate Score CommentCount Body
2 Dec 15, 2008 7 3 <b>variable</b>
Table-值函数如果感兴趣
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,'[[',']]')
*/