Metodo migliore per implementare una ricerca filtrata


17

Vorrei chiederti la tua opinione quando si tratta di implementare un modulo di ricerca filtrato. Immaginiamo il seguente caso:

  • 1 grande tavolo con molte colonne
  • Potrebbe essere importante dire che questo SQL Server

Devi implementare un modulo per cercare i dati in questa tabella e in questo modulo avrai diverse caselle di controllo che ti permetteranno di personalizzare questa ricerca.

Ora la mia domanda qui è quale delle seguenti dovrebbe essere il modo migliore per implementare la ricerca?

  1. Creare una procedura memorizzata con una query all'interno. Questa procedura memorizzata verificherà se i parametri sono forniti dall'applicazione e nel caso in cui non vengano forniti un carattere jolly verrà inserito nella query.

  2. Creare una query dinamica, costruita in base a ciò che viene fornito dall'applicazione.

Lo sto chiedendo perché so che SQL Server crea un piano di esecuzione quando viene creata la procedura memorizzata, al fine di ottimizzarne le prestazioni, tuttavia creando una query dinamica all'interno della procedura memorizzata sacrificheremo l'ottimizzazione ottenuta dal piano di esecuzione?

Per favore, dimmi quale sarebbe l'approccio migliore nella tua opinione.


Di seguito affermi che stai tendendo alla soluzione dinamica. È fantastico, assicurati di enumerare i possibili filtri e di avere indici che li supportano. Finché le query sono costruite in modo coerente, dovrebbero essere efficienti.
Matthew Flynn,

Risposte:


10

Potresti voler guardare la risposta a questa domanda simile qui: /programming/11329823/add-where-clauses-to-sql-dynamically-programmmatic

Abbiamo scoperto che uno SPROC che accetta un sacco di parametri opzionali e implementa il filtro in questo modo:

CREATE PROC MyProc (@optionalParam1 NVARCHAR(50)=NULL, @optionalParam2 INT=NULL)
AS 
...
SELECT field1, field2, ... FROM [Table]
WHERE 
  (@optionalParam1 IS NULL OR MyColumn1 = @optionalParam1)
  AND (@optionalParam2 IS NULL OR MyColumn2 = @optionalParam2)

memorizzerà nella cache il primo piano di esecuzione con cui viene eseguito (ad es. @optionalParam1 = 'Hello World', @optionalParam2 = NULL) ma poi eseguirà miseramente se gli passiamo un diverso set di parametri opzionali (ad es @optionalParam1 = NULL, @optionalParam2 = 42.). (E ovviamente vogliamo l'esecuzione del piano memorizzato nella cache, quindi WITH RECOMPILEè uscito)

L'eccezione qui è che se sulla query è presente almeno un filtro OBBLIGATORIO che è ALTAMENTE selettivo e adeguatamente indicizzato, oltre ai parametri opzionali, il PROC sopra funzionerà bene.

Tuttavia, se TUTTI i filtri sono opzionali, la verità piuttosto terribile è che il sql dinamico parametrizzato in realtà funziona meglio (a meno che non si scriva N! PROCS statici diversi per ogni permutazione dei parametri opzionali).

SQL dinamico come il seguente creerà e memorizzerà nella cache un piano diverso per ogni permutazione dei parametri della query, ma almeno ogni piano sarà "adattato" alla query specifica (non importa se si tratta di un PROC o di un Adhoc SQL - come purché si tratti di query con parametri, verranno memorizzate nella cache)

Quindi da qui la mia preferenza per:

DECLARE @SQL NVARCHAR(MAX)        

-- Mandatory / Static part of the Query here
SET @SQL = N'SELECT * FROM [table] WHERE 1 = 1'

IF @OptionalParam1 IS NOT NULL        
    BEGIN        
        SET @SQL = @SQL + N' AND MyColumn1 = @optionalParam1'    
    END        

IF @OptionalParam2 IS NOT NULL        
    BEGIN        
        SET @SQL = @SQL + N' AND MyColumn2 = @optionalParam2'    
    END        

EXEC sp_executesql @SQL,        
    N'@optionalParam1 NVARCHAR(50), 
      @optionalParam2 INT'
    ,@optionalParam1 = @optionalParam1
    ,@optionalParam2 = @optionalParam2

ecc. Non importa se passiamo i parametri ridondanti in sp_executesql: vengono ignorati. Vale la pena notare che gli ORM come Linq2SQL e EF usano sql dinamico parametrizzato in modo simile.


1
Sì, l'ho pensato, questa era l'opzione che ho scelto. Volevo solo assicurarmi che fosse buono. Grazie per la risposta.
j0N45,

"Se un'istruzione SQL viene eseguita senza parametri, SQL Server parametrizza l'istruzione internamente per aumentare la possibilità di abbinarla a un piano di esecuzione esistente. Questo processo è chiamato semplice parametrizzazione." Quindi sostanzialmente il programma può usare qualcosa come "dove filenumber =" + nomefile. Certo, questo apre una lattina di vermi ma questo è un argomento diverso ;-)
Codismo

5

Inizia con qualunque cosa pensi sia più facile da implementare (suppongo che l'opzione 2). Quindi misurare le prestazioni per i dati del mondo reale. Inizia a ottimizzare solo quando necessario, non in anticipo.

A proposito, a seconda della complessità dei filtri di ricerca, l'attività potrebbe non essere risolta facilmente senza SQL dinamico. Quindi, anche quando si utilizza una procedura memorizzata, molto probabilmente non aumenterà le prestazioni, come già sospetti. D'altra parte, se aiuta, ci sono diversi tipi di suggerimenti (vedi http://www.simple-talk.com/sql/performance/controlling-execution-plans-with-hints/ ) che puoi aggiungere a un SQL query, dinamica o no, per aiutare il server SQL a ottimizzare il suo piano di esecuzione.


Bene, ho già implementato l'opzione 2 e penso che sia il modo migliore, principalmente perché i caratteri jolly ridurranno drasticamente le prestazioni, tuttavia sto sacrificando la manutenzione, perché ciò aumenterà la complessità del codice. Volevo solo sapere se qualcuno conosce un'opzione migliore per questo tipo di situazioni.
j0N45,

Ti darei un voto positivo, ma non mi dispiace per la reputazione.
j0N45,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.