2012-07-04 49 views
11

如何以編程方式將搜索條件添加到SQL存儲過程? 在我的應用程序(C#)我使用存儲過程(SQL服務器2008 R2)動態/編程向SQL中添加WHERE子句

ALTER PROCEDURE [dbo].[PROC001] 
@userID varchar(20), 
@password varchar(20) 
AS 
SELECT * FROM tUsers WHERE RTRIM(Name) = @userID AND RTRIM(Password) = @password 

我想更多的條件來擴展此查詢的,現在我不知道有多少條件將使用此查詢因程序執行。2,3,6或20。我要添加這些條件編程,如:

SELECT * FROM tUsers WHERE RTRIM(Name) = @userID AND RTRIM(Password) = @password 
AND Field2 = '1' AND Field3 = '0' OR Field4 <> '8' AND Field5 < '100' .... 

是否有可能發送到動態存儲過程的條件?

+0

可能的重複:http://stackoverflow.com/questions/977021/can-a-stored-procedure-have-dynamic-parameters-to-be-used-in-an-in-clause – phadaphunk

回答

4

編輯 - 基於偏好LINQ ORM的,如果可能的話

如果你不需要在ADO中這樣做,更好的解決方案是使用ORM,它將最終構建參數rized ad-hoc sql。這是兩全其美的 - 你可以獲得動態查詢的靈活性,沒有冗餘過濾器來優化優化器,查詢計劃本身是可緩存的,而且你可以安全地避免注入攻擊等問題。和基於的LINQ ORM查詢,可以輕易閱讀:

// Build up a non-materialized IQueryable<> 
var usersQuery = db.Users; 
if (!string.IsNullOrEmpty(userID)) 
{ 
     usersQuery = usersQuery.Where(u => u.Name == userId); 
} 
// Of course, you wouldn't dream of storing passwords in cleartext. 
if (!string.IsNullOrEmpty(anotherField)) 
{ 
     usersQuery = usersQuery.Where(u => u.AnotherColumn == anotherField); 
} 
... 
// Materialize (and execute) the query 
var filteredUsers = usersQuery.ToList(); 

對於複雜的查詢,你可能想看看PredicateBuilder

ADO /手動查詢建築

您可以使用sp_executesql來按照以下動態構建SQL。只要你參數化你應該安全的變量,像SQL注入和轉義引號等將會爲你處理。

CREATE PROCEDURE [dbo].[PROC001] 
    @userID varchar(20), 
    @password varchar(20), 
    @optionalParam1 NVARCHAR(50) = NULL -- Other optional parameters 
AS   
    BEGIN   
     SET NOCOUNT ON   

     DECLARE @SQL NVARCHAR(MAX)   

     -- Mandatory/Static part of the Query here. 
     -- Cleartext passwords are verboten, and RTRIM is redundant in filters 
     SET @SQL = N'SELECT * FROM tUsers WHERE Name = @userID AND PwdHash = @pwdHash' 

     IF @OptionalParam1 IS NOT NULL   
      BEGIN   
       SET @SQL = @SQL + N' AND AnotherField = @OptionalParam1'  
      END   

     EXEC sp_executesql @SQL,   
      N'@userID varchar(20), 
      @pwdHash varchar(20), 
      @optionalParam1 NVARCHAR(50)' 
      ,@userID = @userID 
      ,@pwdHash = @pwdHash 
      ,@optionalParam1 = @optionalParam1 
    END 

回覆,爲什麼WHERE (@x IS NULL OR @x = Column)一個壞主意?

(從我下面的評論)

雖然「可選參數」模式運作良好的「瑞士軍刀」的小桌子使用時,查詢可選的過濾器的排列的羣衆,不幸的是,大這會導致針對查詢的所有排列的過濾器產生單個查詢計劃,由於parameter sniffing problem的原因,這會導致可選參數的某些排列組的查詢性能較差。如果可能的話,你應該完全消除冗餘過濾器。

回覆:爲什麼應用功能謂詞是一個壞主意

例如

WHERE SomeFunction(Column) = @someParameter 

使用的謂詞功能經常不夠格由RDBMS("non-sargable")使用索引。

在這種情況下,RTRIM是不必要的,因爲Sql服務器ignores尾隨空格during comparison

16

您可以在SQL只有這樣做,是這樣的:

SELECT * 
FROM tUsers 
WHERE 1 = 1 
    AND (@userID IS NULL OR RTRIM(Name) = @userID) 
    AND (@password IS NULL OR RTRIM(Password) = @password) 
    AND (@field2 IS NULL OR Field2 = @field2) 
.... 

如果傳遞給存儲過程與NULL值的任何參數,那麼整個狀況將被忽略。

注意:我爲了使查詢工作情況下,沒有傳遞給查詢,在這種情況下alll結果集參數添加WHERE 1 = 1將被退回,因爲1 = 1始終是真實的。

+3

+1 This works適用於小型表格,但請注意大型表格,這會導致針對查詢的所有過濾器排列的單個查詢計劃,這可能會導致查詢性能較差,且某些可選參數會發生變化。如果可能的話,你應該完全消除冗餘過濾器。 – StuartLC

+0

你認爲@StuartLC對他的論點是否正確。動態SQL在這裏是更好的選擇嗎? –

+1

更好的解決方案是使用一個ORM,它將最終構建**參數化的即席SQL。這是兩全其美的 - 你可以獲得動態查詢的靈活性,沒有冗餘過濾器來優化優化器,查詢計劃本身是可緩存的,而且你可以安全地避免注入攻擊等問題。而基於Linq的ORM查詢使得閱讀起來更容易。 – StuartLC

2

您可以將您的過程作爲字符串發送,並使用條件,連接和可執行文件發送字符串。

ALTER PROCEDURE [dbo].[PROC001] @userID varchar(20), @password varchar(20), @WhereToAdd varchar(MAX) AS 

exec ('SELECT * FROM tUsers WHERE RTRIM(Name) = @userID AND RTRIM(Password) = @password AND ' + @WhereToAdd) 
2

爲什麼不在您的C#代碼中創建Where子句並將其傳遞給存儲過程。

在C#不喜歡它:

string SQLWhere = " AND Field2 = '1' AND Field3 = '0' OR Field4 <> '8' AND Field5 < '100'"; 

然後把它傳遞給存儲過程,並利用它在你的存儲過程是這樣的:

SELECT * FROM tUsers WHERE RTRIM(Name) = @userID AND RTRIM(Password) = @password + @SQLWhere 
+8

不這樣做的一個原因是要避免可能的SQL注入攻擊媒介。 –