将参数传递给声明的变量 T-SQL
Passing parameters to declated variable T-SQL
我在存储过程中有一个小版本的问题,我想将参数传递给如下:
create procedure VariableTest
@Date1 DATETIME,
@Date2 DATETIME,
@CustomName1 NVARCHAR(100),
@CustomNum2 INT
AS
DECLARE @Condition NVARCHAR(MAX)
DECLARE @SQL NVARCHAR(MAX)
IF(LEN(@CustomName1) > 0)
SET @Condition = 'CustomerName = ' + @CustomName1;
IF(LEN(@CustomNum2) > 0 AND @CustomNum2 > 0)
SET @Condition = 'ClientNumber = ' + @CustomNum2;
SET @SQL =
'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between ' + @Date1 + ' AND ' + @Date2 + '
AND ' + @Condition + '
ORDER BY ex.Sort'
exec @SQL
我 运行 遇到的问题是,当我打印 sql 时,日期周围没有引号并且它们是日期时间格式,另一个问题是在当 CustomerName 可用时,客户名称也没有引号出现...
我怎样才能在 sql 字符串中包含那些传递参数的引号?
选择的解决方案:
create procedure VariableTest
@Date1 DATETIME,
@Date2 DATETIME,
@CustomName1 NVARCHAR(100),
@CustomNum2 INT
AS
DECLARE @Condition NVARCHAR(MAX)
DECLARE @SQL NVARCHAR(MAX)
IF(LEN(@CustomName1) > 0)
SET @Condition = 'CustomerName = @MyVal1';
IF(LEN(@CustomNum2) > 0 AND @CustomNum2 > 0)
SET @Condition = 'ClientNumber = @MyVal2';
DECLARE @MyParams NVARCHAR(200)
SET @MyParams = N'@date1 datetime, @date2 datetime, @MyVal1 NVARCHAR(200), @MyVal2 int';
SET @SQL =
'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between @date1 AND @date2
AND ' + @Condition + '
ORDER BY ex.Sort'
EXECUTE exec sp_executesql
@SQL,
@MyParams,
@date1 = @Date1,
@date2 = @Date2,
@MyVal1 = @CustomName1,
@MyVal2 = @CustomNum2;
Jeroen 发布的 link 是使用动态 SQL 的极好资源,非常值得一读(尤其是为了避免上述注入攻击)。
也就是说,你这里要用的是系统存储过程sp_executesql。这个想法是你基本上可以传入(和传出)变量,所以如果你不需要,你不必连接它们。快速肮脏的解释是你声明一个额外的变量来保存你想要传入的参数的声明,然后你执行 sp_executesql 指示 sql 文本(第一个参数),哪个变量保存你的参数声明(第二个参数)和你声明的变量(第 3 个到第 n 个参数)。这是关于 sp_executesql:
的 MSDN 文档
https://msdn.microsoft.com/en-us/library/ms188001.aspx
那么,您的代码将如下所示:
create procedure VariableTest
@Date1 DATETIME,
@Date2 DATETIME,
@CustomName1 NVARCHAR(100),
@CustomNum2 INT
AS
DECLARE @Condition NVARCHAR(MAX)
DECLARE @SQL NVARCHAR(MAX)
declare @parameters nvarchar(2000)
IF(LEN(@CustomName1) > 0)
SET @Condition = 'CustomerName = ' + @CustomName1;
IF(LEN(@CustomNum2) > 0 AND @CustomNum2 > 0)
SET @Condition = 'ClientNumber = ' + @CustomNum2;
SET @SQL =
'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between @Date1 AND @Date2
AND ' + @Condition + '
ORDER BY ex.Sort'
set @parameters = '@date1 datetime, @date2 datetime'
exec sp_executesql
@sql,
@params,
@date1,
@date2
您可以在没有动态查询的情况下执行此操作
SELECT * FROM MyExampleTable ex
WHERE DateEx1 between @Date1 AND @Date2
AND ((LEN(@CustomName1) > 0 AND CustomerName = @CustomName1)
or (LEN(@CustomNum2) > 0 AND @CustomNum2 > 0 and ClientNumber = @CustomNum2))
ORDER BY ex.Sort
考虑到如果满足 IF
子句,则应在 where
子句中应用这两个条件
动态查询。
您需要再添加一些引号才能得到 date
周围的引号
IF( Len(@CustomName1) > 0 )
SET @Condition = 'CustomerName = ''' + @CustomName1 + '';
IF( Len(@CustomNum2) > 0
AND @CustomNum2 > 0 )
SET @Condition = 'ClientNumber = '
+ CONVERT(VARCHAR(50), @CustomNum2);
SET @SQL = 'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between '''
+ CONVERT(VARCHAR(50), @Date1) + ''' AND '''
+ CONVERT(VARCHAR(50), @Date2) + '''
AND '
+ @Condition + '
ORDER BY ex.Sort'
exec(@SQL)
您的程序容易 sql 注入,避免连接参数并使用像这样的参数化查询....
CREATE PROCEDURE VariableTest
@Date1 DATETIME,
@Date2 DATETIME,
@CustomName1 NVARCHAR(100),
@CustomNum2 INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT * FROM MyExampleTable '
+ N' WHERE DateEx1 between @Date1 AND @Date2 '
+ CASE WHEN LEN(@CustomName1) > 0
THEN N' AND CustomerName = @CustomName1 ' ELSE N'' END
+ CASE WHEN LEN(@CustomNum2) > 0 AND @CustomNum2 > 0
THEN N' AND ClientNumber = @CustomNum2 ' ELSE N'' END
+ N' ORDER BY [Sort] '
exec sp_executesql @SQL
,N'@Date1 DATETIME, @Date2 DATETIME
@CustomName1 NVARCHAR(100), @CustomNum2 INT '
,@Date1
,@Date2
,@CustomName1
,@CustomNum2
END
我不是特别喜欢你选择的解决方案。
NoDisplayName 通过不使用动态查询得到了正确的结果。使用动态查询意味着您不会获得 SP 查询计划缓存的好处。
非动态查询还有其他好处,因为如果您创建带有查询错误的 SP,它会产生编译问题。动态 SQL 隐藏了所有内容。
您唯一会遇到性能问题的情况是您使用的是 LIKE 语句。 “%ABC”、"AB%C" 和 "ABC%" 等值都使用不同的查询计划。您可以在使用 equals 时安全地缓存查询计划。
这就是我要使用的:
SELECT *
FROM MyExampleTable ex
WHERE DateEx1 between @Date1 AND @Date2
AND ((LEN(@CustomName1) > 0 AND CustomerName = @CustomName1)
OR (@CustomNum2 > 0 and ClientNumber = @CustomNum2))
ORDER BY ex.Sort
如果参数不包含任何内容,我也会使参数传递 NULL。
我在存储过程中有一个小版本的问题,我想将参数传递给如下:
create procedure VariableTest
@Date1 DATETIME,
@Date2 DATETIME,
@CustomName1 NVARCHAR(100),
@CustomNum2 INT
AS
DECLARE @Condition NVARCHAR(MAX)
DECLARE @SQL NVARCHAR(MAX)
IF(LEN(@CustomName1) > 0)
SET @Condition = 'CustomerName = ' + @CustomName1;
IF(LEN(@CustomNum2) > 0 AND @CustomNum2 > 0)
SET @Condition = 'ClientNumber = ' + @CustomNum2;
SET @SQL =
'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between ' + @Date1 + ' AND ' + @Date2 + '
AND ' + @Condition + '
ORDER BY ex.Sort'
exec @SQL
我 运行 遇到的问题是,当我打印 sql 时,日期周围没有引号并且它们是日期时间格式,另一个问题是在当 CustomerName 可用时,客户名称也没有引号出现...
我怎样才能在 sql 字符串中包含那些传递参数的引号?
选择的解决方案:
create procedure VariableTest
@Date1 DATETIME,
@Date2 DATETIME,
@CustomName1 NVARCHAR(100),
@CustomNum2 INT
AS
DECLARE @Condition NVARCHAR(MAX)
DECLARE @SQL NVARCHAR(MAX)
IF(LEN(@CustomName1) > 0)
SET @Condition = 'CustomerName = @MyVal1';
IF(LEN(@CustomNum2) > 0 AND @CustomNum2 > 0)
SET @Condition = 'ClientNumber = @MyVal2';
DECLARE @MyParams NVARCHAR(200)
SET @MyParams = N'@date1 datetime, @date2 datetime, @MyVal1 NVARCHAR(200), @MyVal2 int';
SET @SQL =
'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between @date1 AND @date2
AND ' + @Condition + '
ORDER BY ex.Sort'
EXECUTE exec sp_executesql
@SQL,
@MyParams,
@date1 = @Date1,
@date2 = @Date2,
@MyVal1 = @CustomName1,
@MyVal2 = @CustomNum2;
Jeroen 发布的 link 是使用动态 SQL 的极好资源,非常值得一读(尤其是为了避免上述注入攻击)。
也就是说,你这里要用的是系统存储过程sp_executesql。这个想法是你基本上可以传入(和传出)变量,所以如果你不需要,你不必连接它们。快速肮脏的解释是你声明一个额外的变量来保存你想要传入的参数的声明,然后你执行 sp_executesql 指示 sql 文本(第一个参数),哪个变量保存你的参数声明(第二个参数)和你声明的变量(第 3 个到第 n 个参数)。这是关于 sp_executesql:
的 MSDN 文档https://msdn.microsoft.com/en-us/library/ms188001.aspx
那么,您的代码将如下所示:
create procedure VariableTest
@Date1 DATETIME,
@Date2 DATETIME,
@CustomName1 NVARCHAR(100),
@CustomNum2 INT
AS
DECLARE @Condition NVARCHAR(MAX)
DECLARE @SQL NVARCHAR(MAX)
declare @parameters nvarchar(2000)
IF(LEN(@CustomName1) > 0)
SET @Condition = 'CustomerName = ' + @CustomName1;
IF(LEN(@CustomNum2) > 0 AND @CustomNum2 > 0)
SET @Condition = 'ClientNumber = ' + @CustomNum2;
SET @SQL =
'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between @Date1 AND @Date2
AND ' + @Condition + '
ORDER BY ex.Sort'
set @parameters = '@date1 datetime, @date2 datetime'
exec sp_executesql
@sql,
@params,
@date1,
@date2
您可以在没有动态查询的情况下执行此操作
SELECT * FROM MyExampleTable ex
WHERE DateEx1 between @Date1 AND @Date2
AND ((LEN(@CustomName1) > 0 AND CustomerName = @CustomName1)
or (LEN(@CustomNum2) > 0 AND @CustomNum2 > 0 and ClientNumber = @CustomNum2))
ORDER BY ex.Sort
考虑到如果满足 IF
子句,则应在 where
子句中应用这两个条件
动态查询。
您需要再添加一些引号才能得到 date
IF( Len(@CustomName1) > 0 )
SET @Condition = 'CustomerName = ''' + @CustomName1 + '';
IF( Len(@CustomNum2) > 0
AND @CustomNum2 > 0 )
SET @Condition = 'ClientNumber = '
+ CONVERT(VARCHAR(50), @CustomNum2);
SET @SQL = 'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between '''
+ CONVERT(VARCHAR(50), @Date1) + ''' AND '''
+ CONVERT(VARCHAR(50), @Date2) + '''
AND '
+ @Condition + '
ORDER BY ex.Sort'
exec(@SQL)
您的程序容易 sql 注入,避免连接参数并使用像这样的参数化查询....
CREATE PROCEDURE VariableTest
@Date1 DATETIME,
@Date2 DATETIME,
@CustomName1 NVARCHAR(100),
@CustomNum2 INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT * FROM MyExampleTable '
+ N' WHERE DateEx1 between @Date1 AND @Date2 '
+ CASE WHEN LEN(@CustomName1) > 0
THEN N' AND CustomerName = @CustomName1 ' ELSE N'' END
+ CASE WHEN LEN(@CustomNum2) > 0 AND @CustomNum2 > 0
THEN N' AND ClientNumber = @CustomNum2 ' ELSE N'' END
+ N' ORDER BY [Sort] '
exec sp_executesql @SQL
,N'@Date1 DATETIME, @Date2 DATETIME
@CustomName1 NVARCHAR(100), @CustomNum2 INT '
,@Date1
,@Date2
,@CustomName1
,@CustomNum2
END
我不是特别喜欢你选择的解决方案。
NoDisplayName 通过不使用动态查询得到了正确的结果。使用动态查询意味着您不会获得 SP 查询计划缓存的好处。
非动态查询还有其他好处,因为如果您创建带有查询错误的 SP,它会产生编译问题。动态 SQL 隐藏了所有内容。
您唯一会遇到性能问题的情况是您使用的是 LIKE 语句。 “%ABC”、"AB%C" 和 "ABC%" 等值都使用不同的查询计划。您可以在使用 equals 时安全地缓存查询计划。
这就是我要使用的:
SELECT *
FROM MyExampleTable ex
WHERE DateEx1 between @Date1 AND @Date2
AND ((LEN(@CustomName1) > 0 AND CustomerName = @CustomName1)
OR (@CustomNum2 > 0 and ClientNumber = @CustomNum2))
ORDER BY ex.Sort
如果参数不包含任何内容,我也会使参数传递 NULL。