我有一个如下所示的存储过程:
create stored procedure aaa
@columnName nvarchar(10),
@comparisonParam nvarchar(10),
@val nvarchar(100)
as
declare @date date
set @date = convert(@val, date)
exec('select * from Sheep where ' + @columnName + @comparisonParam + @date )
实际上查询应该是这样的:
select * from Sheep where birth_date = 12-12-2000
当我运行该程序时,它不能使用日期值,但使用string和int它可以工作。
答案 0 :(得分:1)
必须引用日期值。
另一方面,我警告不要这样做。如果需要构建动态sql,则需要考虑以下风险:sql注入攻击,语法错误,语义无效等。
考虑使用现有组件来构建查询。几个例子:
.NET LINQ(到SQL /实体)http://msdn.microsoft.com/en-us/library/bb397926.aspx
.NET SqlCommandBuilder http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommandbuilder.aspx
见Best way of constructing dynamic sql queries in C#/.NET3.5?
答案 1 :(得分:0)
您的日期文字需要用单引号括起来(我通常使用CHAR(39)因为它更容易阅读而且不需要转义)。否则你说:
WHERE birth_date = (12) - (12) - (2000)
哪个解析为:
WHERE birth_date = -2000
哪个解析为DATEADD(DAY, -2000, '1900-01-01')
或:
WHERE birth_date = '1894-07-11'
这可能不会产生你想要的结果。
当然有典型的SQL注入警告,假设@columnName
总是一个字符串或日期/时间列,这就是我重写存储过程的方法(虽然我可能会尽量避免如果可以的话,完全是动态SQL。
ALTER PROCEDURE dbo.aaa
@columnName NVARCHAR(10),
@comparisonParam NVARCHAR(10),
@val NVARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX);
SET @sql = N'SELECT * FROM dbo.Sheep WHERE '
+ QUOTENAME(@columnName) + @comparisonParam + CHAR(39)
+ REPLACE(@val, CHAR(39), CHAR(39) + CHAR(39))
+ CHAR(39);
EXEC sp_executesql @sql;
END
GO
为了阻止潜在问题,您可能需要为列和数据类型添加验证,并确保操作符合您的预期。 e.g。
CREATE PROCEDURE dbo.bbb
@columnName NVARCHAR(10),
@comparisonParam NVARCHAR(10),
@val NVARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @delimiter CHAR(1);
SELECT @delimiter = CASE
WHEN [system_type_id] IN
(104,48,52,56,127,59,60,62,106,108,122) THEN '' -- numeric
WHEN [system_type_id] IN
(35,40,41,42,43,58,61,99,167,175,231,239) THEN CHAR(39) -- string
END FROM sys.columns WHERE [object_id] = OBJECT_ID(N'dbo.Sheep')
AND name = @columnName;
IF @delimiter IS NULL
BEGIN
RAISERROR('Column ''%s'' was not found or an unexpected data type.', 11, 1,
@columnName);
RETURN;
END
IF @comparisonParam NOT IN (N'=', N'>=', N'<=', N'<', N'>', N'LIKE')
BEGIN
RAISERROR('Comparison param ''%s'' was not valid.', 11, 1, @comparisonParam);
RETURN;
END
DECLARE @sql NVARCHAR(MAX);
SET @sql = N'SELECT * FROM dbo.Sheep WHERE '
+ QUOTENAME(@columnName) + ' ' + @comparisonParam + ' '
+ @delimiter + REPLACE(@val, CHAR(39), CHAR(39) + CHAR(39))
+ @delimiter;
EXEC sp_executesql @sql;
END
GO
现在确保为字符串文字使用明确的日期格式。 12-12-2000
不是一个好选择。 20001212
要好得多。
如果没有动态SQL,可能有一些方法可以做到这一点 - 我给了very simplified answer here。这可能是可行的,具体取决于数据类型,潜在列数以及您要支持的操作数。
答案 2 :(得分:0)
使用类型化日期参数构建动态SQL。使用sp_executesql
允许将参数定义和参数值传递给嵌入式SQL:
create procedure aaa
@columnName nvarchar(10),
@comparisonParam nvarchar(10),
@val nvarchar(100)
as
declare @date date, @sql nvarchar(max);
set @date = convert(@val, date);
-- Note how @date is a *variable* in the generated SQL:
set @sql =N'select * from Sheep where ' +
quotename(@columnName) + @comparisonParam + N'@date';
-- Use sp_executesql and define the type and value of the variable
exec sp_executesql @sql, N'@date date', @date;
答案 3 :(得分:0)
create stored procedure aaa
@columnName nvarchar(10),
@comparisonParam nvarchar(10),
@val nvarchar(100)
as
declare @date date
set @date = convert(@val, date)
exec('select * from Sheep where ' + @columnName + @comparisonParam + @date )
答案 4 :(得分:-2)
您需要为此创建表值函数,而不是创建存储过程。
您可以使用任何表值函数,如
SELECT * from dbo.CallMyFunction(parameter1, parameter2
例如
CREATE FUNCTION Sales.ufn_SalesByStore (@storeid int)
RETURNS TABLE
AS
RETURN
(
SELECT P.ProductID, P.Name, SUM(SD.LineTotal) AS 'Total'
FROM Production.Product AS P
JOIN Sales.SalesOrderDetail AS SD ON SD.ProductID = P.ProductID
JOIN Sales.SalesOrderHeader AS SH ON SH.SalesOrderID = SD.SalesOrderID
JOIN Sales.Customer AS C ON SH.CustomerID = C.CustomerID
WHERE C.StoreID = @storeid
GROUP BY P.ProductID, P.Name
);
GO
请参阅此参考资料http://msdn.microsoft.com/en-us/library/ms191165(v=sql.105).aspx
修改强>
而不是使用动态sql尝试思考
SELECT * FROM
FROM [dbo].[Person]
WHERE ([PersonID] = @PersonID
OR @AreaID IS NULL
)
AND (([Code] BETWEEN @Code AND CHAR(255))
OR @Code IS NULL
)
AND (([Name] BETWEEN @Name AND CHAR(255))
OR @Name IS NULL
)
AND (([Notes] BETWEEN @Notes AND CHAR(255))
OR @Notes IS NULL
)