2012-07-04 20 views
11

¿Cómo puedo agregar la condición de búsqueda a SQL Stored Procedure mediante programación? En mi solicitud (C#) Estoy usando el procedimiento almacenado (SQL Servidor 2008R2)Agregar cláusulas WHERE a SQL dinámicamente/programáticamente

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

quiero extender esta consulta por más condiciones, y ahora no sé cuántos condiciones va a utilizar esta consulta debido la ejecución del programa .. 2, 3, 6 ó 20. Quiero añadir estas condiciones mediante programación como:

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

¿es posible enviado a las condiciones de procedimiento almacenado de forma dinámica?

+0

Posible duplicado: http://stackoverflow.com/questions/977021/can-a-stored-procedure-have-dynamic-parameters-to-be-used-in-an-in-clause – phadaphunk

Respuesta

4

Editar - Preferencia por LINQ basa ORM, si es posible

Si no necesita hacer esto en ADO, una mejor solución es usar un ORM que finalmente construirá el parámetro rizado ad-hoc sql. Esto es lo mejor de ambos mundos: obtienes la flexibilidad de una consulta dinámica, sin filtros redundantes para alterar el optimizador, el plan de consulta en sí es almacenable en caché y estás a salvo de desagradables ataques de inyección. Y una consulta ORM basado en LINQ hace para facilitar la lectura:

// 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(); 

Para consultas complejas, es posible que desee ver en PredicateBuilder edificio/consulta manual de

ADO

Puede utilizar sp_executesql a construir SQL dinámicamente como se muestra a continuación. Siempre que usted parametrice las variables, debe estar a salvo de problemas como la inyección de SQL y las comillas de escape, etc. que se manejarán por usted.

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, ¿por qué es WHERE (@x IS NULL OR @x = Column) una mala idea?

(de mi comentario a continuación)

Aunque el patrón 'parámetro opcional' funciona bien como una 'navaja suiza' para consultar una multitud de permutaciones de filtros opcionales cuando se utiliza en pequeñas mesas, por desgracia, para los grandes tablas, esto da como resultado un único plan de consulta para todas las permutaciones de filtros para la consulta, lo que puede dar como resultado un bajo rendimiento de consulta con ciertas permutaciones de parámetros opcionales debido al parameter sniffing problem. Si es posible, debe eliminar por completo los filtros redundantes.

Re: ¿Por qué es la aplicación de funciones en los predicados una mala idea

por ejemplo,

WHERE SomeFunction(Column) = @someParameter 

Uso de funciones en predicados descalifica con frecuencia el uso de índices por el RDBMS ("non-sargable").

En este caso, RTRIM es innecesario como servidor Sql ignores espacios finales during comparison.

16

Usted puede hacer esto en SQL solamente, como esto:

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

Si cualquier parámetro pasado al procedimiento almacenado con un valor NULL continuación, se ignorará todo el estado.

Tenga en cuenta que: He añadido WHERE 1 = 1 con el fin de hacer el trabajo de consulta en caso de que no parámetro pasado a la consulta y en este caso felizmente el conjunto de resultados será devuelto, ya que 1 = 1 siempre es cierto.

+3

+1 Esto funciona bien para tablas pequeñas, pero tenga en cuenta que las tablas son grandes, esto da como resultado un único plan de consulta para todas las permutaciones de filtros para la consulta, lo que puede dar como resultado un rendimiento de consulta deficiente con ciertas permutaciones de parámetros opcionales. Si es posible, debe eliminar por completo los filtros redundantes. – StuartLC

+0

¿Cree que @StuartLC es correcto en su argumento? ¿El sql dinámico es la mejor opción aquí? –

+1

La mejor solución es usar un ORM que finalmente construirá ** SQL ** parametric-parameter-parameter. Esto es lo mejor de ambos mundos: obtienes la flexibilidad de una consulta dinámica, sin filtros redundantes para alterar el optimizador, el plan de consulta en sí es almacenable en caché y estás a salvo de desagradables ataques de inyección. Y una consulta ORM basada en Linq facilita la lectura. – StuartLC

2

puede tener su procedimiento como cadena y enviar una cadena con las condiciones, concatenar y ejecutar.

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

¿Por qué no crea su cláusula Where en su código C# y la pasa al procedimiento almacenado.

En su C# hacerlo como:

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

luego pasar esta al procedimiento almacenado, y lo utilizan en su procedimiento almacenado de esta manera:

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

Una razón para NO hacer esto sería evitar posibles vectores de ataque de inyección SQL. –

Cuestiones relacionadas