该代码的目标是动态运行查询,如果在列中没有数据行,则返回0;如果在列中有数据行,则返回1。这是我的存储过程代码:
ALTER proc [dbo].[usp_ColumnFieldValidator]
(
@TblName nvarchar(30),
@ColumnName nvarchar(30),
@RetVal bit output
)
as
begin
declare @CountOfRowsQuery as nvarchar(300)
set @CountOfRowsQuery = 'select count('+quotename(@ColumnName)+') from '+quotename(@TblName)+' having count(' +quotename(@ColumnName)+') = nullif(count('+quotename(@ColumnName)+'),0)'
execute sp_executesql @CountOfRowsQuery
select @RetVal = dbo.fn_ColumnValidator(@CountOfRowsQuery)
end
如您所见,正在调用用户定义的函数来设置@RetVal的值。这是我的用户定义函数代码。
ALTER function [dbo].[fn_ColumnValidator]
(
@NullChecker as nvarchar(max)
)
returns bit
as
begin
declare @returnVar as bit
if @NullChecker is null
set @returnVar = 0
else
set @returnVar = 1
return @returnVar
end
@ RetVal的输出始终为1,我将此错误归因于@CountOfRowsQuery,它存储整个字符串而不是查询的值,即:@CountOfRowsQuery = null,如果行数为零,否则@CountOfRowsQuery =数字列中存在的行数。为了使事情更清楚,我在运行程序时附加了输出的屏幕截图。
如清单2所示,sp返回null,但function_returned_value设置为1而不是0。
代码的目标是动态运行查询,如果在列中没有数据行,则返回0;如果在列中有数据行,则返回1。
伙计,如果这不是过度复杂,我不知道这是什么。这是一个更简单(更有效)的查询来完成工作:
SELECT CAST(IIF(EXISTS(
SELECT 1
FROM TableName
WHERE ColumnName IS NOT NULL
)), 1, 0) As Bit)
现在,要将其更改为使用动态SQL的过程,而不会将您暴露给SQL Injection威胁,您可以这样做:
ALTER PROCEDURE [dbo].[usp_ColumnFieldValidator]
(
@TblName sysname,
@ColumnName sysname,
@RetVal bit output
)
AS
BEGIN
IF NOT EXISTS(
SELECT 1
FROM Information_Schema.Columns
WHERE Table_Name = @TblName
AND Column_Name = @ColumnName
)
RETURN;
DECLARE @Sql nvarchar(1000) =
N'SELECT @RetVal = CAST(IIF(EXISTS(
SELECT 1
FROM '+ QUOTENAME(@TblName) + N'
WHERE '+ QUOTENAME(@ColumnName) + N' IS NOT NULL
)), 1, 0) As Bit)'
EXEC sp_executesql @Sql, N'@RetVal bit output', @RetVal OUTPUT;
END
主要注意事项:
我将@TblName
和@ColumnName
变量更改为数据类型sysname
,而不是原始的nvarchar(30)
-因为这是SQL Server在内部用于存储标识符的数据类型。
由于无法参数化标识符,因此我将其列入白名单。
我正在使用sp_executeSql
将动态查询的值直接返回到我的输出参数中。
有关动态SQL的更多技巧,您可以阅读我的博客文章,标题为The do’s and don’ts of dynamic SQL for SQL Server。