Se possibile, non farlo.
Questa è la risposta: è un anti-pattern. Se il client conosce la tabella da cui desidera i dati, allora SELECT FROM ThatTable
. Se un database è progettato in modo tale da renderlo necessario, sembra essere progettato in modo non ottimale. Se un livello di accesso ai dati ha bisogno di sapere se esiste un valore in una tabella, è facile comporre SQL in quel codice e inserire questo codice nel database non va bene.
A me sembra come installare un dispositivo all'interno di un ascensore dove si può digitare il numero del piano desiderato. Dopo aver premuto il pulsante Vai, sposta una lancetta meccanica sul pulsante corretto per il piano desiderato e lo preme. Questo introduce molti potenziali problemi.
Nota: non c'è intenzione di deridere, qui. Il mio sciocco esempio di ascensore è stato * il miglior dispositivo che potessi immaginare * per evidenziare sinteticamente i problemi con questa tecnica. Aggiunge un livello inutile di indirezione, spostando la scelta del nome della tabella da uno spazio del chiamante (utilizzando un DSL, SQL robusto e ben compreso) in un ibrido che utilizza codice SQL lato server oscuro / bizzarro.
Tale suddivisione delle responsabilità attraverso lo spostamento della logica di costruzione delle query in SQL dinamico rende il codice più difficile da comprendere. Viola una convenzione standard e affidabile (come una query SQL sceglie cosa selezionare) in nome del codice personalizzato irto di potenziali errori.
Ecco i punti dettagliati su alcuni dei potenziali problemi con questo approccio:
Dynamic SQL offre la possibilità di SQL injection che è difficile da riconoscere nel codice front-end o nel solo codice back-end (è necessario ispezionarli insieme per vederlo).
Le procedure e le funzioni archiviate possono accedere alle risorse per le quali il proprietario dell'SP / funzione ha diritti, ma il chiamante no. Per quanto ho capito, senza particolari cure, quindi per impostazione predefinita quando si utilizza codice che produce SQL dinamico e lo esegue, il database esegue l'SQL dinamico sotto i diritti del chiamante. Ciò significa che non sarai in grado di utilizzare affatto gli oggetti privilegiati, oppure dovrai aprirli a tutti i client, aumentando la superficie di potenziale attacco ai dati privilegiati. L'impostazione della funzione SP / al momento della creazione in modo che venga sempre eseguita come un determinato utente (in SQL Server EXECUTE AS
) può risolvere il problema, ma rende le cose più complicate. Ciò aggrava il rischio di SQL injection menzionato nel punto precedente, rendendo l'SQL dinamico un vettore di attacco molto allettante.
Quando uno sviluppatore deve capire cosa sta facendo il codice dell'applicazione per modificarlo o correggere un bug, troverà molto difficile ottenere l'esatta query SQL in esecuzione. È possibile utilizzare il profiler SQL, ma ciò richiede privilegi speciali e può avere effetti negativi sulle prestazioni sui sistemi di produzione. La query eseguita può essere registrata dall'SP, ma ciò aumenta la complessità per un beneficio discutibile (che richiede di accogliere nuove tabelle, eliminare i vecchi dati, ecc.) Ed è abbastanza non ovvio. In effetti, alcune applicazioni sono progettate in modo tale che lo sviluppatore non dispone delle credenziali del database, quindi diventa quasi impossibile per lui vedere effettivamente la query inviata.
Quando si verifica un errore, ad esempio quando si tenta di selezionare una tabella che non esiste, verrà visualizzato un messaggio del tipo "nome oggetto non valido" dal database. Ciò accadrà esattamente allo stesso modo sia che tu stia componendo l'SQL nel back-end o nel database, ma la differenza è che un povero sviluppatore che sta cercando di risolvere i problemi del sistema deve scrutare un livello più in profondità in un'altra caverna sotto quella in cui il esiste il problema, scavare nella procedura miracolosa che fa tutto per cercare di capire qual è il problema. I log non mostreranno "Errore in GetWidget", mostreranno "Errore in OneProcedureToRuleThemAllRunner". Questa astrazione generalmente peggiorerà un sistema .
Un esempio in pseudo-C # di cambio di nomi di tabelle in base a un parametro:
string sql = $"SELECT * FROM {EscapeSqlIdentifier(tableName)};"
results = connection.Execute(sql);
Anche se questo non elimina ogni possibile problema immaginabile, i difetti che ho delineato con l'altra tecnica sono assenti da questo esempio.