我有一个动态存储过程,必须在SQL Server 2008的case语句中动态添加where子句。
我的程序如下: -
CREATE PROCEDURE SPGETDATA
@STRNAME NVARCHAR(100),
@STRCODE NVARCHAR(100)
AS
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**
END
用户可以选择@strname或@strcode。但不是一次两个。
在这种情况下,一个类似的声明是可以的,但替代方案始终是查询的负担,因为它总是如
@STRNAME like '%%'
或如下
@STRCODE like '%%'
现在,如果我使用这种方法,编译器会花费一些时间来搜索'%%',即使没有什么可匹配的,还是会绕过它并且不需要任何费用?我也检查了执行计划,但它没有为like子句显示任何内容。
因此我必须在webApps中使用它,因此sp的速度必须考虑。该表有数百万行。
两者的执行计划都是相同的。如果我在查询中使用类似cluase或从查询中删除它显示 - Clustred index sacn 100%。
请帮忙。
答案 0 :(得分:2)
首先,如果其中一列可以为空,则测试col LIKE '%%'
不是No-Op,但实际上相当于测试列col IS NOT NULL
,这可能不是所需的效果。
其次,如果列不可为空,那么它确实是No-Op,那么这不是一个特别好的方法,因为SQL Server将不优化检查。有关更好的方法,请参阅Dynamic Search Conditions in 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)
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=''
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)'
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)'
答案 1 :(得分:1)
你可以这样写:
IF COALESCE(RTRIM(LTRIM(@STRNAME))), '') <> ''
BEGIN
SELECT myTable.*
FROM myTable
WHERE IsDELETED = 0
AND STRNAME LIKE '%'+ @STRNAME + '%'
END
ELSE -- IF COALESCE(RTRIM(LTRIM(@STRCODE))), '') <> ''
BEGIN
SELECT myTable.*
FROM myTable
WHERE IsDELETED = 0
AND STRCODE LIKE '%'+ @STRCODE + '%'
END
无论如何,使用LIKE会阻止DB使用索引。这将是您最高的执行成本。
答案 2 :(得分:0)
我会创建两个版本的查询,一个用LIKE '%%'
,另一个没有,然后比较他们的执行计划。