在like子句中搜索野生字符'%%'的编译器成本

4 人关注

我有一个动态存储过程,必须在SQL Server 2008的case语句中动态地添加where子句。

我的程序如下。-

CREATE PROCEDURE SPGETDATA
@STRNAME NVARCHAR(100),
@STRCODE NVARCHAR(100)
BEGIN
SELECT  myTable.*
FROM myTable
WHERE 
IsDELETED = 0
AND STRNAME LIKE CASE WHEN (RTRIM(LTRIM(@STRNAME))) <> '' THEN
'%'+ @STRNAME + '%' ELSE '%%' END
AND STRCODE LIKE CASE WHEN (RTRIM(LTRIM(@STRCODE)) <> '') THEN
'%' + @STRCODE + '%' ELSE '%%' END**

用户可以选择@stname或@strcode。但不能同时选择两者。

在这种情况下,一个类似的语句是可以的,但另一种方式总是比查询有负担,因为它总是作为

@STRNAME like '%%'

或如下所示

@STRCODE like '%%'

现在,如果我使用这种方法,编译器是否会花费一些时间来搜索像'%%',即使没有任何匹配,还是会绕过它而不花费任何费用?我也检查了执行计划,但它没有显示类似子句的内容。

因此,我必须在webApps中使用它,所以必须考虑sp的速度。而且这个表有几百万行。

两者的执行计划都是一样的。如果我在查询中使用聚类或从查询中删除它,它显示 - 聚类索引sacn 100%。

3 个评论
你在这里做了相当多的编辑。请注意,你在试图改进问题的同时,不要大幅度改变问题的范围,因为你已经收到了好几个答案。我删除了这个问题的维基状态。
谢谢 @TimPost ,我以后也会注意的,谢谢。你能告诉我维基状态是什么吗?
社区维基 "意味着不会从帖子的投票中获得(或失去)声誉,并且帖子可以被只有100声誉的用户编辑。更多信息请参见常见问题中的 这一条 。维基状态对于需要随时间发展的答案来说是非常好的。如果任何帖子(问题或答案)被大量编辑,它也会自动应用,以阻止人们通过编辑 "撞 "他们的帖子。你无意中触发了这个安全防护措施,我把它恢复了,因为这显然是无意的。
sql
sql-server
sql-server-2008
Gopal Krishna Ranjan
Gopal Krishna Ranjan
发布于 2011-12-05
3 个回答
Martin Smith
Martin Smith
发布于 2011-12-06
已采纳
0 人赞同

首先,如果其中任何一列是空的,那么测试 col LIKE '%%' 并不是一个No-Op,而是实际上相当于测试 col IS NOT NULL 不是空 的,这很可能不是想要的效果。

其次,如果该列不是空的,所以它确实是一个No-Op,那么这不是一个特别好的方法,因为SQL Server 不会 优化检查。请参阅 T-SQL中的动态搜索条件 ,了解更好的方法。

不过,在你的情况下,有可能不会有太大的区别。

因为你总是在做一个带前导通配符的搜索,针对一个或其他列,并且你在做 SELECT * ,那么无论如何,你可能会以全表扫描结束。

一种情况是,你在一个或多个列上有一个较窄的索引,可以优先于扫描整个聚类索引/表。然而,即使在这种情况下,SQL Server仍然需要做书本标记查询来检索 * ,所以对于那个特定的查询,可以很便宜地评估剩余谓词。

然而,生成的计划将完全不适合其他参数的调用,所以这种尝试性的全面查询可能会给你带来参数嗅探的问题,如下图所示。

CREATE TABLE myTable
id int primary key,
STRNAME VARCHAR(100) NOT NULL,
STRCODE VARCHAR(100) NOT NULL,
IsDELETED BIT NOT NULL DEFAULT 0,
Filler CHAR(7000) NULL,
INSERT INTO myTable(id, STRNAME, STRCODE)
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)), 
       ISNULL(name,type), 
       ISNULL(name,type)
FROM master..spt_values
CREATE INDEX ix ON myTable(STRNAME)

首先调用名称参数(扫描次数1,逻辑读数7)。

EXEC sp_executesql N'
SELECT  myTable.*
FROM myTable
WHERE 
IsDELETED = 0  
AND STRNAME LIKE CASE WHEN (RTRIM(LTRIM(@STRNAME))) <> '''' THEN 
''%''+ @STRNAME + ''%'' ELSE ''%%'' END 
AND STRCODE LIKE CASE WHEN (RTRIM(LTRIM(@STRCODE)) <> '''') THEN 
''%'' + @STRCODE + ''%''  ELSE ''%%'' END 
N'@STRNAME NVARCHAR(100),
@STRCODE NVARCHAR(100)
', @STRNAME = '(rpc)', @STRCODE=''

用代码参数调用,重复使用同一计划(扫描次数1,逻辑读数7690)。

EXEC sp_executesql N'
SELECT  myTable.*
FROM myTable
WHERE 
IsDELETED = 0  
AND STRNAME LIKE CASE WHEN (RTRIM(LTRIM(@STRNAME))) <> '''' THEN 
''%''+ @STRNAME + ''%'' ELSE ''%%'' END 
AND STRCODE LIKE CASE WHEN (RTRIM(LTRIM(@STRCODE)) <> '''') THEN 
''%'' + @STRCODE + ''%''  ELSE ''%%'' END 
N'@STRNAME NVARCHAR(100),
@STRCODE NVARCHAR(100)
', @STRNAME = '', @STRCODE='(rpc)'

用代码参数调用生成特定的计划(扫描计数1,逻辑读数2517)。

EXEC sp_executesql N'
SELECT  myTable.*
FROM myTable
WHERE 
IsDELETED = 0  
AND STRNAME LIKE CASE WHEN (RTRIM(LTRIM(@STRNAME))) <> '''' THEN 
''%''+ @STRNAME + ''%'' ELSE ''%%'' END 
AND STRCODE LIKE CASE WHEN (RTRIM(LTRIM(@STRCODE)) <> '''') THEN 
''%'' + @STRCODE + ''%''  ELSE ''%%'' END 
OPTION (RECOMPILE)
N'@STRNAME NVARCHAR(100),
@STRCODE NVARCHAR(100)
', @STRNAME = '', @STRCODE='(rpc)'
Wim
Wim
发布于 2011-12-06
0 人赞同

你可以这样写。

IF COALESCE(RTRIM(LTRIM(@STRNAME))), '') <> ''
BEGIN
SELECT  myTable.*
FROM myTable
  WHERE IsDELETED = 0
AND STRNAME LIKE '%'+ @STRNAME + '%'
ELSE -- IF COALESCE(RTRIM(LTRIM(@STRCODE))), '') <> '' 
BEGIN
SELECT  myTable.*
FROM myTable
  WHERE IsDELETED = 0
AND STRCODE LIKE '%'+ @STRCODE + '%'