2012-07-14 103 views
-4

我有一個存儲過程,看起來像這樣比較日期:在存儲過程中的exec語句

create stored procedure aaa @culumnName nvarchar(10), @comparasionParam nvarchar(10), 
    @val nvarchar(100) as 

declare @date date 
set @date = convert(@val, date) 

exec('select * from Sheep where ' + @columnName + @comparasionParam + @date) 

當實際的查詢應該是這樣的:

select * from Sheep where birth_date = 12-12-2000 

當我運行procudure它不適用於日期值,但與字符串和整數它的作品。 請幫助我這是迫切的。

+0

是否使用C#,vb.net? – 2012-07-14 09:45:12

回答

-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 
      ) 
+0

傢伙'我有一個問題'我的列名的一部分程序削減'爲什麼這樣? – 2012-07-23 05:21:34

+0

@RahellyGelfanboim對於一個字符串列,我已經使用了[[Code] BETWEEN @Code AND CHAR(255)) 或@Code IS NULL'來進行字符串過濾。使用'='代替'BETWEEN',ie'[Code] = @Code或@Code IS NULL' – 2012-07-23 05:43:01

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?

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 - 我給了very simplified answer here。根據數據類型,潛在列的數量以及要支持的操作數量,這可能是可行的。

+1

要向下投票,請解釋爲什麼 – 2012-07-15 14:54:35

0

使用類型化日期參數構建動態SQL。使用sp_executesql它允許參數的定義和參數值傳遞給嵌入式SQL:

create stored procedure aaa 
    @culumnName nvarchar(10), 
    @comparasionParam 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) + @comparasionParam + N'@date'; 

-- Use sp_executesql and define the type and value of the variable 
exec sp_executesql @sql, N'@date date', @date; 
+0

我沒有使用參數的理由是因爲我不相信所指定的列總是會成爲一個日期。 – 2012-07-15 14:42:14

+0

@aaron我沒有考慮過,沒錯。我想表明,有一種方法可以通過強大的類型來完全避免區域設置問題。當然,參數類型應該與列類型匹配以增加可捕獲性。 – 2012-07-15 14:46:44

+0

如果將日期作爲字符串傳遞給@val,則區域設置仍可能是問題。 – 2012-07-15 16:32:40

0
create stored procedure aaa @culumnName nvarchar(10), @comparasionParam nvarchar(10), 
    @val nvarchar(100) as 

declare @date date 
set @date = convert(@val, date) 

exec('select * from Sheep where ' + @columnName + @comparasionParam + @date)