动态/编程地向SQL添加WHERE子句

如何以编程方式将搜索条件添加到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' .... 

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

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

如果您不需要在ADO中执行此操作,更好的解决方案是使用ORM,它最终将构建参数化的ad-hoc sql。 这是两全其美的 – 您可以获得动态查询的灵活性,没有冗余filter来扰乱优化器,查询计划本身是可缓存的,并且您可以免受注入攻击等恶意攻击。 基于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 

Re,为什么WHERE (@x IS NULL OR @x = Column)是个坏主意?

(摘自我的评论)

虽然“可选参数”模式可以很好地用作“瑞士军刀”,用于在小型表格上查询可选filter的多种排列,但遗憾的是,对于大型表格,这会导致针对所有filter排列的单个查询计划查询,由于参数嗅探问题 ,可能导致查询性能较差以及可选参数的某些排列。 如果可能,您应该完全消除冗余filter。

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

例如

 WHERE SomeFunction(Column) = @someParameter 

在谓词中使用函数经常会使RDBMS使用索引失去资格( “不可思考” )。

在这种情况下, RTRIM是不必要的,因为Sql服务器在比较期间 忽略尾随空格。

你可以只在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始终为true。

你可以将你的过程作为字符串,并发送一个包含条件,连接和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) 

为什么不在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