La domanda originale era "Come posso parametrizzare una query ..."
Consentitemi di affermare che questo è non una risposta alla domanda originale. Ne esistono già alcune dimostrazioni in altre buone risposte.
Detto questo, vai avanti e contrassegna questa risposta, ridimensionala, contrassegnala come non una risposta ... fai quello che ritieni giusto.
Vedi la risposta di Mark Brackett per la risposta preferita che io (e altri 231) abbiamo votato. L'approccio fornito nella sua risposta consente 1) per un uso efficace delle variabili di bind e 2) per predicati che sono rilevanti.
Risposta selezionata
Quello che voglio affrontare qui è l'approccio dato nella risposta di Joel Spolsky, la risposta "selezionata" come la risposta giusta.
L'approccio di Joel Spolsky è intelligente. E funziona in modo ragionevole, mostrerà comportamenti prevedibili e prestazioni prevedibili, dati valori "normali" e con i casi limite normativi, come NULL e la stringa vuota. E può essere sufficiente per una particolare applicazione.
Ma in termini di generalizzazione di questo approccio, consideriamo anche i casi d'angolo più oscuri, come quando la Name
colonna contiene un carattere jolly (come riconosciuto dal predicato LIKE). Il carattere jolly che vedo più comunemente usato è %
(un segno di percentuale). Quindi affrontiamolo qui ora, e poi passiamo ad altri casi.
Alcuni problemi con il carattere%
Considera un valore Nome di 'pe%ter'
. (Per gli esempi qui, utilizzo un valore di stringa letterale al posto del nome della colonna.) Una riga con un valore Nome di '' pe% ter 'verrebbe restituita da una query del modulo:
select ...
where '|peanut|butter|' like '%|' + 'pe%ter' + '|%'
Ma quella stessa riga non verrà restituita se l'ordine dei termini di ricerca viene invertito:
select ...
where '|butter|peanut|' like '%|' + 'pe%ter' + '|%'
Il comportamento che osserviamo è strano. La modifica dell'ordine dei termini di ricerca nell'elenco modifica il set di risultati.
È quasi ovvio che potremmo non voler pe%ter
abbinare il burro di arachidi, non importa quanto gli piaccia.
Custodia angolare oscura
(Sì, concordo sul fatto che questo è un caso oscuro. Probabilmente uno che non è probabile che venga testato. Non ci aspetteremmo un carattere jolly in un valore di colonna. Potremmo presumere che l'applicazione impedisca la memorizzazione di tale valore. Ma nella mia esperienza, ho visto raramente un vincolo di database che non consentiva specificamente caratteri o modelli che sarebbero considerati caratteri jolly sul lato destro di un LIKE
operatore di confronto.
Rattoppare un buco
Un approccio per correggere questo buco è quello di sfuggire al %
carattere jolly. (Per chiunque non abbia familiarità con la clausola di escape sull'operatore, ecco un link alla documentazione di SQL Server .
select ...
where '|peanut|butter|'
like '%|' + 'pe\%ter' + '|%' escape '\'
Ora possiamo abbinare la% letterale. Naturalmente, quando abbiamo un nome di colonna, dovremo sfuggire dinamicamente al jolly. Possiamo usare la REPLACE
funzione per trovare le occorrenze del %
personaggio e inserire una barra rovesciata davanti a ciascuna, in questo modo:
select ...
where '|pe%ter|'
like '%|' + REPLACE( 'pe%ter' ,'%','\%') + '|%' escape '\'
Questo risolve il problema con il carattere jolly%. Quasi.
Sfuggire alla fuga
Riconosciamo che la nostra soluzione ha introdotto un altro problema. Il personaggio di fuga. Vediamo che avremo anche bisogno di sfuggire a qualsiasi occorrenza del personaggio di fuga stesso. Questa volta, usiamo il! come personaggio di fuga:
select ...
where '|pe%t!r|'
like '%|' + REPLACE(REPLACE( 'pe%t!r' ,'!','!!'),'%','!%') + '|%' escape '!'
Anche il trattino basso
Ora che siamo su un rotolo, possiamo aggiungere un altro REPLACE
handle al carattere jolly di sottolineatura. E solo per divertimento, questa volta useremo $ come personaggio di escape.
select ...
where '|p_%t!r|'
like '%|' + REPLACE(REPLACE(REPLACE( 'p_%t!r' ,'$','$$'),'%','$%'),'_','$_') + '|%' escape '$'
Preferisco questo approccio alla fuga perché funziona in Oracle, MySQL e SQL Server. (Di solito uso la barra rovesciata come carattere di escape, poiché quello è il personaggio che usiamo nelle espressioni regolari. Ma perché essere vincolato dalla convenzione!
Quelle fastidiose parentesi
SQL Server consente inoltre di trattare i caratteri jolly come letterali racchiudendoli tra parentesi []
. Quindi non abbiamo ancora risolto, almeno per SQL Server. Poiché le coppie di parentesi hanno un significato speciale, dovremo sfuggire anche a quelle. Se riusciamo a sfuggire correttamente alle parentesi, almeno non dovremo preoccuparci del trattino -
e del carato ^
all'interno delle parentesi. E possiamo lasciare qualsiasi %
e_
caratteri all'interno delle parentesi fuggiti, dal momento che avremo praticamente disattivato il significato speciale delle staffe.
Trovare coppie di parentesi corrispondenti non dovrebbe essere così difficile. È un po 'più difficile che gestire le occorrenze di singleton% e _. (Nota che non è sufficiente sfuggire a tutte le occorrenze delle parentesi, perché una parentesi singleton è considerata letterale e non ha bisogno di essere sfuggita. La logica sta diventando un po 'più confusa di quanto io possa gestire senza eseguire più casi di test .)
L'espressione incorporata diventa confusa
Quell'espressione incorporata nell'SQL sta diventando più lunga e brutta. Probabilmente possiamo farlo funzionare, ma il paradiso aiuta la povera anima che viene dietro e deve decifrarla. Per quanto io sia un fan delle espressioni in linea, sono propenso a non usarne uno qui, principalmente perché non voglio lasciare un commento che spieghi il motivo del disordine e mi scusi per questo.
Una funzione dove?
Ok, quindi, se non lo gestiamo come espressione incorporata nell'SQL, l'alternativa più vicina che abbiamo è una funzione definita dall'utente. E sappiamo che non accelererà le cose (a meno che non possiamo definire un indice su di esso, come avremmo potuto fare con Oracle). Se dobbiamo creare una funzione, potremmo farlo nel codice che chiama SQL dichiarazione.
E quella funzione può avere alcune differenze nel comportamento, a seconda del DBMS e della versione. (Un grido a tutti voi sviluppatori Java così entusiasti di poter usare qualsiasi motore di database in modo intercambiabile.)
Conoscenza del dominio
Potremmo avere una conoscenza specializzata del dominio per la colonna, (ovvero l'insieme dei valori consentiti applicati per la colonna. Potremmo sapere a priori che i valori memorizzati nella colonna non conterranno mai un segno di percentuale, un carattere di sottolineatura o una parentesi coppie. In tal caso, includiamo solo un breve commento sul fatto che tali casi sono coperti.
I valori memorizzati nella colonna possono consentire% o _ caratteri, ma un vincolo può richiedere la fuga di tali valori, magari utilizzando un carattere definito, in modo che i valori siano COME un confronto "sicuro". Ancora una volta, un breve commento sul set di valori consentito, e in particolare su quale personaggio viene utilizzato come personaggio di escape, e segui l'approccio di Joel Spolsky.
Ma, in assenza di conoscenze specialistiche e di una garanzia, è importante per noi almeno prendere in considerazione la gestione di quegli oscuri casi angolari e valutare se il comportamento sia ragionevole e "secondo le specifiche".
Altre questioni ricapitolate
Credo che altri abbiano già sufficientemente sottolineato alcune delle altre aree di interesse comunemente considerate:
Iniezione di SQL (prendendo quelle che sembrano essere le informazioni fornite dall'utente e includendole nel testo SQL anziché fornirle tramite variabili di bind. L'uso delle variabili di bind non è necessario, è solo un comodo approccio per contrastare l'iniezione di SQL. Ci sono altri modi per affrontarlo:
piano di ottimizzazione che utilizza la scansione dell'indice anziché la ricerca dell'indice, possibile necessità di un'espressione o di una funzione per evitare caratteri jolly (possibile indice sull'espressione o sulla funzione)
l'utilizzo di valori letterali al posto delle variabili di bind influisce sulla scalabilità
Conclusione
Mi piace l'approccio di Joel Spolsky. È intelligente. E funziona
Ma non appena l'ho visto, ho subito visto un potenziale problema con esso, e non è mia natura lasciarlo scivolare. Non intendo essere critico nei confronti degli sforzi degli altri. So che molti sviluppatori prendono il loro lavoro molto sul personale, perché investono così tanto in esso e si preoccupano così tanto. Quindi, per favore, capisci, questo non è un attacco personale. Quello che sto identificando qui è il tipo di problema che cresce nella produzione piuttosto che testare.
Sì, sono andato molto lontano dalla domanda originale. Ma dove altro lasciare questa nota riguardo a ciò che considero un problema importante con la risposta "selezionata" per una domanda?