-4

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

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

当我运行该过程时,它不适用于日期值,但可以使用字符串和 int。

4

5 回答 5

1

必须引用日期值。

在旁注中,我会警告不要这样做。如果您需要构建动态 sql,则需要考虑以下风险:sql 注入攻击、语法错误、语义无效等。

考虑使用现有组件来构建查询。几个例子:

.NET LINQ (to SQL/Entities) http://msdn.microsoft.com/en-us/library/bb397926.aspx

.NET SqlCommandBuilder http://msdn.microsoft.com/en-us/library /system.data.sqlclient.sqlcommandbuilder.aspx


请参阅在 C#/.NET3.5 中构建动态 sql 查询的最佳方式?

于 2012-07-14T08:38:14.677 回答
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 )
于 2013-02-26T06:36:15.573 回答
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

为了阻止潜在的问题,您可能希望为列和数据类型添加验证,并确保该操作是您所期望的。例如

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 的情况下,可能有一些方法可以做到这一点——我在这里给出了一个非常简化的答案。这可能是可行的,具体取决于数据类型、潜在列的数量以及您想要支持的操作数量。

于 2012-07-14T15:43:47.147 回答
0

Build your dynamic SQL using a typed date parameter. Use sp_executesql which allows to pass parameter definitions and parameter values to the embedded 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;
于 2012-07-14T20:08:48.040 回答
-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
            )
于 2012-07-14T08:37:59.763 回答