Добавление предложений WHERE к SQL динамически/программно

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

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' ....

Можно ли динамически отправлять условия в хранимую процедуру?

Ответ 1

Изменить - предпочтение ORM на основе LINQ, если возможно

Если вам не нужно делать это в ADO, лучшим решением будет использовать ORM, который в конечном итоге будет создавать параметризованный 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) плохая идея?

(Из моего комментария ниже)

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

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

например.

WHERE SomeFunction(Column) = @someParameter

Использование функций в предикатах часто дисквалифицирует использование индексов с помощью СУБД ( "non-sargable" ).

В этом случае RTRIM не требуется, поскольку сервер Sql игнорирует конечные пробелы во время сравнения.

Ответ 2

Вы можете сделать это только в 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

у вас может быть ваша процедура как строка и отправить строку с условиями, 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)

Ответ 4

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

В вашем С# выполните следующие действия:

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