2012-07-04 4 views
11

Как я могу добавить условие поиска в SQL Хранимую процедуру программно? В моем приложении (C#) Я использую хранимую процедуру (SQL Server 2008R2)Добавить предложения WHERE для SQL динамически/программно

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

Edit - Preference для LINQ на основе ORM, если возможно

Если вам не нужно это делать в ADO, лучшим решением является использование ORM, который в конечном итоге будет создавать параметр red ad-hoc sql. Это лучшее из обоих миров - вы получаете гибкость динамического запроса, без избыточных фильтров, чтобы расстроить оптимизатор, сам план запроса можно кэшировать, и вы можете быть в безопасности от настиг, таких как инъекции. И ORM запрос Linq на основе делает для легкого чтения:

// 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 

Re, почему WHERE (@x IS NULL OR @x = Column) плохая идея?

(От мой комментарий ниже)

Хотя «необязательный параметр» модель хорошо работает как «швейцарский армейский нож» для запрашивая множество перестановок дополнительных фильтров при использовании на небольших таблицах, к сожалению, большой таблицы, это приводит к единому плану запроса для всех перестановок фильтров для запроса, что может привести к низкой производительности запросов с некоторыми перестановками необязательных параметров из-за parameter sniffing problem. Если возможно, вы должны полностью исключить избыточные фильтры.

Re: Почему применение функций в предикатах плохая идея

например

WHERE SomeFunction(Column) = @someParameter 

Использование функций в предикатах часто лишает использование индексов по РСУБД ("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 тогда все условие будет игнорироваться.

Обратите внимание, что: Я добавил WHERE 1 = 1 для того, чтобы сделать работу запроса в случае, если ни один параметр, передаваемый в запросе, и в этом случае ALLL результирующего набора будет возвращено, так как 1 = 1 всегда верно.

+3

+1 Это работает хорошо для небольших таблиц, но обратите внимание на большие таблицы, это приводит к единому плану запроса для всех перестановок фильтров для запроса, что может привести к низкой производительности запросов с некоторыми перестановками необязательных параметров. Если возможно, вы должны полностью исключить избыточные фильтры. – StuartLC

+0

Считаете ли вы, что @StuartLC верна в своих аргументах. Является ли динамический sql лучшим вариантом здесь? –

+1

Лучшее решение - использовать ORM, который в конечном итоге будет строить ** параметризованный ** ad-hoc sql. Это лучшее из обоих миров - вы получаете гибкость динамического запроса, без избыточных фильтров, чтобы расстроить оптимизатор, сам план запроса можно кэшировать, и вы можете быть в безопасности от настиг, таких как инъекции. И запрос ORM на основе Linq упрощает чтение. – StuartLC

2

у вас может быть ваша процедура как строка и отправить строку с условиями, concatenate и exec.

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

Почему бы вам не создать предложение Where в коде C# и передать его в хранимую процедуру.

В вашем 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-инъекций. –

Смежные вопросы