2012-07-04 31 views
11

Come è possibile aggiungere la condizione di ricerca alla stored procedure SQL a livello di codice? Nella mia applicazione (C#) che sto utilizzando stored procedure (SQL Server 2008R2)Aggiungere clausole WHERE a SQL in modo dinamico/programmatico

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

voglio estendere questa query da più condizioni, e ora non so quante condizioni utilizzerà questa query a causa l'esecuzione del programma .. 2, 3, 6 o 20. voglio aggiungere queste condizioni di programmazione come:

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

E 'possibile condizioni di stored procedure in modo dinamico inviato?

+0

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

risposta

4

Modifica - Preferenze per LINQ basa ORM, se possibile

Se non è necessario eseguire questa operazione in ADO, una soluzione migliore consiste nell'utilizzare un ORM che alla fine creerà paramete ralizzato ad-hoc sql. Questo è il meglio di entrambi i mondi: si ottiene la flessibilità di una query dinamica, senza filtri ridondanti per sconvolgere l'ottimizzatore, il piano di query stesso è memorizzabile nella cache e si è al sicuro da virus come attacchi di iniezione. E una query ORM Linq-based rende per una facile lettura:

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

Per le query complesse, si consiglia di guardare PredicateBuilder

ADO edificio/manuale di query

È possibile utilizzare sp_executesql per creare SQL in modo dinamico come di seguito. A condizione di parametrizzare le variabili che dovrebbero essere al sicuro da problemi come l'iniezione SQL e le citazioni di escape ecc. Saranno gestite per te.

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, perché è WHERE (@x IS NULL OR @x = Column) una cattiva idea?

(Dal mio commento qui sotto)

Anche se il modello 'parametro opzionale' funziona bene come un 'coltellino svizzero' per interrogare un gran numero di permutazioni di filtri opzionali quando viene utilizzato su piccoli tavoli, purtroppo, per i grandi tabelle, questo si traduce in un piano di query singolo per tutte le permutazioni dei filtri per la query, che può comportare prestazioni di query scadenti con determinate permutazioni di parametri opzionali a causa dello parameter sniffing problem. Se possibile, è necessario eliminare completamente i filtri ridondanti.

Re: Perché l'applicazione funzioni in predicati una cattiva idea

esempio

WHERE SomeFunction(Column) = @someParameter 

Uso di funzioni in predicati squalifica spesso l'uso di indici dall'RDBMS ("non-sargable").

In questo caso, RTRIM non è necessario come server Sql ignores spazi finali during comparison.

16

Si può fare questo in SQL solo, in questo modo:

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

Se qualsiasi parametro passato alla stored procedure con un valore NULL poi l'intera condizione verrà ignorato.

Nota che: ho aggiunto WHERE 1 = 1 al fine di rendere il lavoro di query nel caso in cui nessun parametro passato alla query e in questo caso alll il set di risultati verrà restituito, in quanto 1 = 1 è sempre vero.

+3

+1 Questo funziona bene per le tabelle di piccole dimensioni, ma si noti per tabelle di grandi dimensioni, questo si traduce in un piano di query singolo per tutte le permutazioni dei filtri per la query, che può comportare prestazioni di query scadenti con determinate permutazioni di parametri opzionali. Se possibile, è necessario eliminare completamente i filtri ridondanti. – StuartLC

+0

Pensi che @StuartLC sia corretto sulla sua argomentazione. SQL dinamico è l'opzione migliore qui? –

+1

La soluzione migliore consiste nell'utilizzare un ORM che alla fine costruirà ** sql parametrizzato ** ad-hoc. Questo è il meglio di entrambi i mondi: si ottiene la flessibilità di una query dinamica, senza filtri ridondanti per sconvolgere l'ottimizzatore, il piano di query stesso è memorizzabile nella cache e si è al sicuro da virus come attacchi di iniezione. E una query ORM basata su Linq semplifica la lettura. – StuartLC

2

potresti avere la tua procedura come stringa e inviare una stringa con le condizioni, concatenare ed eseguire.

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

Perché non creare la clausola Where all'interno del codice C# e trasferirla nella stored procedure.

Nel vostro C# fare così:

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

poi passare questo per la stored procedure e utilizzare nel tuo stored procedure in questo modo:

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

Un motivo per non farlo sarebbe quello di evitare possibili vettori di attacco di iniezione SQL. –

Problemi correlati