2010-01-04 8 views
18

Domanda: Quali sono alcune altre strategie per evitare numeri magici o valori codificati nei propri script SQL o stored procedure?SQL: evitare codici di codifica o numeri magici

Considerare una stored procedure il cui compito è controllare/aggiornare un valore di un record basato sul suo StatusID o un'altra tabella di ricerca FK o un intervallo di valori.

consideri un tavolo Status dove l'ID è più importante, in quanto è un FK a un altro tavolo:

alt text

Gli script SQL che devono essere evitati sono qualcosa come:

DECLARE @ACKNOWLEDGED tinyint 

SELECT @ACKNOWLEDGED = 3 --hardcoded BAD 

UPDATE SomeTable 
SET  CurrentStatusID = @ACKNOWLEDGED 
WHERE ID = @SomeID 

Il problema qui è che questo non è portatile ed è esplicitamente dipendente dal valore hardcoded. Esistono dei difetti sottili quando lo si distribuisce in un altro ambiente con gli inserti di identità disattivati.

cercando anche di evitare una SELECT sulla base della descrizione del testo/nome dello stato:

UPDATE SomeTable 
SET  CurrentStatusID = (SELECT ID FROM [Status] WHERE [Name] = 'Acknowledged') 
WHERE ID = @SomeID 

Domanda: Quali sono alcune altre strategie su come evitare numeri magici o valori hard-coded nei vostri script SQL o stored procedure?

Alcuni altri pensieri su come raggiungere questo obiettivo:

  • aggiungere una nuova colonna di bit (denominata come 'IsAcknowledged') e insiemi di regole in cui ci può essere una sola riga con un valore di 1. Questo aiuterebbe a trovare la fila unica: SELECT ID FROM [Status] WHERE [IsAcknowledged] = 1)

risposta

7

A un certo livello, ci sarà un certo "hard coding" dei valori. L'idea di eliminarli proviene da due lati:

  1. Rendere il codice più leggibile (vale a dire, dicendo 'Acknowledged' piuttosto che 3 è probabilmente andando a fare le vostre intenzioni più evidente al lettore
  2. Rendere il codice. più dinamico, in cui una funzione può avere un parametro piuttosto diverse funzioni che non lo fanno (questa è una semplificazione ovviamente, ma il significato dovrebbe essere abbastanza ovvio comunque)

Fare bit colonne di diverse stat può essere un'idea buona o cattiva; in realtà dipende solo dai dati. Se i dati passano attraverso varie "fasi" (Ricevuto, Riconosciuto, In corso di revisione, Rifiutato, Accettato, Rispondito, ecc.), Tale approccio si ridimensiona rapidamente dalla redditività (per non parlare del processo irritante di dover assicurare che solo delle colonne è impostato su 1 in qualsiasi momento). Se, d'altra parte, lo stato è davvero semplice come lo descrivi, fare ciò può rendere il codice più leggibile e gli indici migliori.

Il più grande valore di codifica non codificabile è valori di codifica hard che fanno riferimento ad altre entità (in altre parole, hard codifica della chiave primaria per un oggetto corrispondente). La stringa 'Acknowledged 'è ancora un valore hard-coded, è solo più trasparente nel suo significato e non è un riferimento a qualcos'altro. Per me, si riduce a questo: se è possibile (ragionevolmente) cercare, farlo. Se non è possibile (o se qualcosa lo rende un compito irragionevole dal punto di vista delle prestazioni o della manutenibilità), codificarlo con cura. Usando questo, puoi cercare il valore di 3 usando Acknowledged; non puoi cercare Acknowledged da altro.

0

Bene per cominciare, Business Logic dovrebbe probabilmente essere evitato nello strato di stoccaggio.

quanto questo sembra essere inevitabile quando si utilizza un DB ad esempio SQL Server in cui un sacco di BL lattina esiste nel DB, penso che si potrebbe invece tornare ad utilizzare identificatori di stringa piuttosto che auto ID.

Questo sarà molto più gestibile rispetto agli ID automatici e può essere gestito in modi migliori, anche quando si utilizza il livello di applicazione effettivo.

Ad esempio utilizzando un approccio Net, un sacco degli identificatori di stringa unico può essere negozio ovunque dai file di configurazione , per le ricerche aggiuntive utilizzando il db selezionato, file XML.

1

Se il tuo caso è semplice come sopra, dove un bit IsAcknowled funzionerebbe, vorrei seguire quella strada. Non ho mai avuto problemi con questo. Se si hanno scenari più complicati, in cui si finirebbero con una dozzina di campi bit, non vedo alcun problema nell'utilizzare un "numero magico" purché si abbia pieno controllo di esso.Se si è preoccupati che la colonna Identity non sia mappata correttamente quando si porta il database, è possibile creare un'altra colonna univoca (non identificata) con i propri valori ID, interi o guid o qualsiasi altra cosa sarebbe utile.

1

Se l'entità "Stato", che fa parte del modello di dominio, ha valori predefiniti, alcuni dei quali devono essere gestiti in modo specifico dalle stored procedure, allora è perfettamente OK per i riferimenti hardcode a tali valori specifici nel tuo codice. Il problema qui è che stai confondendo ciò che è potenzialmente una chiave astratta (colonna Identity ID) per un valore che ha un significato nel tuo modello di dominio. Mentre è OK mantenere la colonna di identità ID, dovresti usare un attributo significativo della tua entità di dominio quando ti riferisci ad esso nel codice, questo può essere il nome o può essere un alias numerico. Ma questo alias numerico dovrebbe essere definito nel tuo modello di dominio, ad es. 3 significa "Riconosciuto" e non deve essere confuso con il campo dell'ID astratto che, come dici tu, potrebbe essere una colonna di identità in alcune delle istanze del tuo database.

3

Questo è come lo farei. (Perché è molto più veloce di quanto il tuo esempio)

UPDATE SomeTable 
SET CurrentStatusID = [Status].[ID] 
FROM SomeTable 
RIGHT JOIN [Status] ON [Name] = 'Acknowledged' 
WHERE SomeTable.[ID] = @SomeID 

(non testato potrebbe avere errori di battitura)

+0

Faccio questo, ma le mie tabelle di ricerca hanno una colonna StatusText che viene utilizzata per la visualizzazione e una colonna UniqueName che viene utilizzata per i valori con hardcoded. Ciò consente alle richieste dell'utente di modificare l'etichetta visualizzata senza eseguire l'UniqueName a cui fanno riferimento i valori codificati. – AaronLS

+0

L'unica ragione per cui temo di utilizzare questa tecnica, è che non so quanto abbia un impatto a lungo termine sulle prestazioni, per eseguire query su una colonna di stringhe invece che su un intero. – AaronLS

+0

Ok, questo è stato 7 mesi fa. Ricordo che il mio punto era usare un join invece di una sottoquery, che sarà più veloce. Non ho idea di dove ti venga l'idea che sto interrogando su una stringa invece che su un intero. Ciò funzionerà meglio dell'esempio nella domanda poiché si tratta di un join e non di una sottoquery. – Hogan

3

non fare affidamento su di identità per tutti gli ID. Ad esempio, se si dispone di una tabella di ricerca che avrà meno di 50 righe, è perfettamente ragionevole definire tali ricerche come aventi ID specifici o utilizzare un codice con valori stringa. In entrambi i casi, "hard-coding" non è più un problema.

9

di recente ho capito che i numeri magici possono essere implemented by views:

CREATE VIEW V_Execution_State AS 
SELECT 10 AS Pending, 20 AS Running, 30 AS Done 

DECLARE @state INT 
SELECT @state = Pending FROM V_Execution_State 
+0

Un'idea carina. Come avere un enum in SQL. Grazie! – Joey

+2

Tuttavia, un problema con questo approccio è che se si decide di aggiungere un altro stato di esecuzione ora non si deve solo cambiare la vista, ma qualsiasi codice che si basa su una struttura statica ne risentirà. –

+0

Mi chiedo se sia possibile utilizzare un pivot per implementare una vista che cambia automaticamente quando la tabella di ricerca ha aggiunte a essa. – AaronLS

12

per situazioni come la vostra tabella di stato, creo quello che io chiamo i set di dati "statici". Queste tabelle contengono dati che

  • è impostato e definito al momento della creazione,
  • Mai cambia mai, e
  • è sempre lo stesso, da istanza di database a un'istanza di database, con senza eccezioni

Cioè, allo stesso tempo si crea la tabella, si popola anche, utilizzando uno script per garantire che i valori siano sempre gli stessi. Successivamente, indipendentemente da dove o quando si trova il database, è possibile conoscere in base ai valori specificati e impostare l'hard-code di conseguenza e in modo appropriato. (Non userei mai le chiavi surrogate o la proprietà della colonna Identity in queste situazioni.)

Non devi usare numeri, puoi usare stringhe - o binari o date, o qualsiasi cosa sia la più semplice, la più semplice e la più appropriata . (Quando posso, io uso le stringhe di carattere - e non varchars - come "RCVD", "DLVR", ACKN ", e così via sono valori hard-coded più facili di, diciamo, 0, 2 e 3.)

Questo sistema funziona per insiemi di valori non estendibili.Se questi valori possono essere modificati (in modo che 0 non significhi più "riconosciuto", allora si ha un problema di accesso di sicurezza.Se si dispone di un sistema in cui possono essere aggiunto dagli utenti, allora avete un problema di progettazione diversa e difficile da risolvere

+1

Concordato.In questi casi creo una colonna StatusCd di in qualche modo il cui valore non cambia mai, in questo modo può effettuare il porting su sistemi, ma essere comprensibile solo guardando il valore del codice – Yoav

+0

Rendi questo valore univoco leggibile dall'uomo la chiave primaria del tavolo? O usi ancora un numero intero come la chiave primaria? – AaronLS

+0

Io uso il valore univoco (essenzialmente la chiave nativa), l'aggiunta di una chiave surrogata a tale tabella è assolutamente inutile. –

3

un'idea:.

CREATE FUNC dbo.CONST_ACKNOWLEDGED() 
RETURNS tinyint 
AS 
BEGIN 
    RETURN 3 
END 

Tuttavia, ha senso solo se non si dispone di autonumber, IMHO

0

Immaginate

table dbo.Status 
(
    Id int PK 
    ,Description varchar 
) 
values 
1, Received 
2, Acknowledged 
3, Under Review 
etc 

Così, proprio

declare @StatusReceived int = 1 
declare @StatusAcknowledged int = 2 
declare @StatusUnderReview = 3 
etc 

Come altri menzionare, questo presuppone che l'identità non è impostato.

Anch'io utilizzato per partecipare alle tabelle di ricerca, ma ciò mantiene la SELECT più breve e più facile da leggere.

Questo approccio si presta all'automazione, quindi creo un'intera tabella in una query separata, quindi copio gli elementi richiesti (non tutti).

Problemi correlati