在exec语句中比较存储过程中的日期

时间:2012-07-14 08:31:22

标签: sql sql-server sql-server-2008

我有一个如下所示的存储过程:

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它可以工作。

5 个答案:

答案 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
            )