当我传入@NameSubstring参数时,以下存储过程正常工作。我知道我没有正确地动态构建like子句。当此参数还需要作为参数传递给过程底部附近的EXEC sp_executesql调用中时,如何构建like子句?
ALTER PROCEDURE [dbo].[spGetAutoCompleteList]
(
@AutoCompleteID int,
@StatusFlag int,
@NameSubstring varchar(100),
@CompanyID int,
@ReturnMappings bit,
@ReturnData bit
)
AS
DECLARE @ErrorCode int,
@GetMappings nvarchar(500),
@Debug bit,
@Select AS NVARCHAR(4000),
@From AS NVARCHAR(4000),
@Where AS NVARCHAR(4000),
@Sql AS NVARCHAR(4000),
@Parms AS NVARCHAR(4000)
SET @ErrorCode = 0
SET @Debug = 1
BEGIN TRAN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
IF @AutoCompleteID IS NOT NULL OR @StatusFlag IS NOT NULL OR @NameSubstring IS NOT NULL
BEGIN
SET @Select = '
SELECT ac.AutoCompleteID,
ac.AutoCompleteName,
ac.CompanyID,
ac.StatusFlag,
ac.OwnerOperID,
ac.CreateDT,
ac.CreateOperID,
ac.UpdateDT,
ac.UpdateOperID,
ac.SubmitOperID,
ac.SubmitDT,
ac.ReviewComments'
SET @GetMappings = '
Select ac.AutoCompleteID'
IF @ReturnData = 1
BEGIN
SET @Select = @Select + '
, ac.AutoCompleteData'
END
SET @From = '
FROM tbAutoComplete ac'
SET @Where = '
WHERE 1=1'
IF @AutoCompleteID IS NOT NULL
BEGIN
SET @Where = @Where + '
AND ac.AutoCompleteID = CAST(@AutoCompleteID AS nvarchar)'
END
IF @StatusFlag IS NOT NULL
BEGIN
SET @Where = @Where + '
AND ac.StatusFlag = CAST(@StatusFlag AS nvarchar)'
END
IF @NameSubstring IS NOT NULL
BEGIN
SET @Where = @Where + '
AND ac.AutoCompleteName like @NameSubstring' + '%'
END
SET @Where = @Where + '
AND ac.CompanyID = + CAST(@CompanyID AS nvarchar)'
SET @Sql = @Select + @From + @Where
SET @Parms = '
@AutoCompleteID int,
@StatusFlag int,
@NameSubstring varchar(100),
@CompanyID int'
EXEC sp_executesql @Sql,
@Parms,
@AutoCompleteID,
@StatusFlag,
@NameSubstring,
@CompanyID
IF @ReturnMappings = 1
BEGIN
SET @GetMappings = 'Select * FROM tbAutoCompleteMap acm WHERE acm.AutoCompleteID IN(' + @GetMappings + @From + @Where + ')'
--EXEC sp_executesql @GetMappings
END
IF @Debug = 1
BEGIN
PRINT @GetMappings
PRINT @Sql
END
END
SELECT @ErrorCode = @ErrorCode + @@ERROR
IF @ErrorCode <> 0
BEGIN
SELECT '<FaultClass>1</FaultClass><FaultCode>1</FaultCode>'
+ '<FaultDesc>Internal Database Error.</FaultDesc>'
+ '<FaultDebugInfo>(spGetAutoCompleteList): There was an error while trying to SELECT from tbAutoComplete.</FaultDebugInfo>'
ROLLBACK TRAN
RETURN
END
COMMIT TRAN
答案 0 :(得分:4)
@NameString需要在引号之外。要使用引号括起@ NameString%,可以使用两个单引号将引号字符转义为文字。
SET @Where = @Where + '
AND ac.AutoCompleteName like ''' + @NameSubstring + '%'''
答案 1 :(得分:0)
SET @Where = @Where +'AND ac.AutoCompleteName like''%'+ @ NameSubstring +'%'''
答案 2 :(得分:0)
那么,您在询问如何在使用动态查询和sp_executesql
时指定参数?
可以这样做:
DECLARE /* ... */
SET @SQLString = N'SELECT @LastlnameOUT = max(lname) FROM pubs.dbo.employee WHERE job_lvl = @level'
SET @ParmDefinition = N'@level tinyint, @LastlnameOUT varchar(30) OUTPUT'
SET @IntVariable = 35
EXECUTE sp_executesql @SQLString, @ParmDefinition, @level = @IntVariable, @LastlnameOUT=@Lastlname OUTPUT
您可以在此处详细了解:http://support.microsoft.com/kb/262499
答案 3 :(得分:0)
如果您不使用动态SQL,也许这不会成为问题。它看起来像一个vanilla查询也可以正常工作,并且更容易阅读和调试。请考虑以下事项:
SELECT ac.AutoCompleteID,
ac.AutoCompleteName,
ac.CompanyID,
ac.StatusFlag,
ac.OwnerOperID,
ac.CreateDT,
ac.CreateOperID,
ac.UpdateDT,
ac.UpdateOperID,
ac.SubmitOperID,
ac.SubmitDT,
ac.ReviewComments
FROM tbAutoComplete ac
WHERE ((ac.AutoCompleteID = CAST(@AutoCompleteID AS nvarchar) OR (@AutoCompleteID IS NULL))
AND ((ac.StatusFlag = CAST(@StatusFlag AS nvarchar)) OR (@StatusFlag IS NULL))
AND ((ac.AutoCompleteName like @NameSubstring + '%') OR (@NameSubstring IS NULL))
AND ((ac.CompanyID = CAST(@CompanyID AS nvarchar)) OR (@CompanyID IS NULL))
这更简单,更清晰等等。祝你好运!
答案 4 :(得分:0)
要避免SQL注入,请在将参数添加到SQL语句时不要使用连接。我强烈建议您使用以下格式:
IF @NameSubstring IS NOT NULL BEGIN
SET @Where += 'AND ac.AutoCompleteName LIKE @NameSubstring + char(37)'
END
使用char(37)
代替'%'
,您可以避免在字符串文字周围转义撇号
如果您想在任意一侧放置通配符,那么您可以使用
IF @NameSubstring IS NOT NULL BEGIN
SET @Where += 'AND ac.AutoCompleteName LIKE char(37) + @NameSubstring + char(37)'
END
如果有人认为我错了,可以证明连接是一种风险。
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestInjection]') AND type in (N'U')) BEGIN
create table TestInjection(ID int, Value nvarchar(10))
insert into TestInjection (ID,Value)
Values
(1,'Tom'),
(2,'Fred'),
(3,'Betty'),
(4,'Betty2'),
(5,'Betty3'),
(6,'George')
END
declare @NameSubstring nvarchar(1000) = 'Bet'
--declare @NameSubstring nvarchar(1000) = 'Bet%'';delete from TestInjection;select * from TestInjection where value = ''x'
declare @ID int = 2
Declare @sql nvarchar(1000) = 'select * from TestInjection where ID > @ID '
SET @sql +=' AND [Value] like ''' + @NameSubstring + '%'''
Declare @params nvarchar(100) = '@ID int'
exec sp_executesql @sql, @params, @ID
select * from TestInjection
第一次运行它,你将获得一个包含3条记录的结果集,另一条包含所有6条记录。
现在将@NameSubstring
的声明交换到替代方案,然后重新运行。表中的所有数据都已删除。
另一方面,如果您编写代码如下:
declare @NameSubstring nvarchar(1000) = 'Bet'
--declare @NameSubstring nvarchar(1000) = 'Bet%'';delete from TestInjection;select * from TestInjection where value = ''x'
declare @ID int = 2
Declare @sql nvarchar(1000) = 'select * from TestInjection where ID > @ID '
SET @sql +=' AND [Value] LIKE @NameSubstring + char(37)'
Declare @params nvarchar(100) = '@ID int, @NameSubstring nvarchar(1000)'
exec sp_executesql @sql, @params, @ID, @NameSubstring
select * from TestInjection
然后您仍然可以获得第一次返回的3条记录,但是当您更改声明时,您不会丢失数据。