14

如何以编程方式将搜索条件添加到 SQL 存储过程?在我的应用程序(C#)中,我使用存储过程(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' ....

是否可以动态地将条件发送到存储过程?

4

3 回答 3

19

您只能在 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是为了使查询工作,以防没有参数传递给查询,在这种情况下,所有结果集都将被返回,因为1 = 1它总是正确的。

于 2012-07-04T13:16:10.857 回答
7

编辑 - 如果可能,首选基于 LINQ 的 ORM

如果您不需要在 ADO 中执行此操作,更好的解决方案是使用 ORM,它最终将构建参数化的 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),
    @pwdHash 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)一个坏主意?

(来自我下面的评论)

尽管“可选参数”模式在小表上使用时可以很好地作为“瑞士军刀”查询可选过滤器的多种排列,但不幸的是,对于大表,这会导致针对所有过滤器排列的单个查询计划查询,由于参数嗅探问题,这可能导致可选参数的某些排列的查询性能不佳。如果可能,您应该完全消除冗余过滤器。

回复:为什么在谓词中应用函数是个坏主意

例如

WHERE SomeFunction(Column) = @someParameter

在谓词中使用函数经常会取消 RDBMS 对索引的使用(“non-sargable”)。

在这种情况下,RTRIM是不必要的,因为 Sql server在比较过程中会忽略尾随空格。

于 2012-07-04T13:29:44.333 回答
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)
于 2012-07-04T13:18:14.170 回答