14

Sono attualmente in fase di progettazione delle tabelle del database per un'applicazione di gestione siti Web di un cliente &. La mia domanda riguarda l'uso delle chiavi primarie come parti funzionali di una tabella (e non l'assegnazione di numeri "ID" a ogni tabella solo perché).Progettazione database e l'utilizzo di chiavi primarie non numeriche

Per esempio, qui ci sono quattro tabelle correlate dal database finora, uno dei quali utilizza il numero chiave primaria tradizionale, gli altri che utilizzano nomi univoci come chiave primaria:

-- 
-- website 
-- 
CREATE TABLE IF NOT EXISTS `website` (
    `name` varchar(126) NOT NULL, 
    `client_id` int(11) NOT NULL, 
    `date_created` timestamp NOT NULL default CURRENT_TIMESTAMP, 
    `notes` text NOT NULL, 
    `website_status` varchar(26) NOT NULL, 
    PRIMARY KEY (`name`), 
    KEY `client_id` (`client_id`), 
    KEY `website_status` (`website_status`), 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

-- 
-- website_status 
-- 
CREATE TABLE IF NOT EXISTS `website_status` (
    `name` varchar(26) NOT NULL, 
    PRIMARY KEY (`name`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 
INSERT INTO `website_status` (`name`) VALUES 
('demo'), 
('disabled'), 
('live'), 
('purchased'), 
('transfered'); 

-- 
-- client 
-- 
CREATE TABLE IF NOT EXISTS `client` (
    `id` int(11) NOT NULL auto_increment, 
    `date_created` timestamp NOT NULL default CURRENT_TIMESTAMP, 
    `client_status` varchar(26) NOT NULL, 
    `firstname` varchar(26) NOT NULL, 
    `lastname` varchar(46) NOT NULL, 
    `address` varchar(78) NOT NULL, 
    `city` varchar(56) NOT NULL, 
    `state` varchar(2) NOT NULL, 
    `zip` int(11) NOT NULL, 
    `country` varchar(3) NOT NULL, 
    `phone` text NOT NULL, 
    `email` varchar(78) NOT NULL, 
    `notes` text NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `client_status` (`client_status`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ; 

-- 
-- client_status 
--- 
CREATE TABLE IF NOT EXISTS `client_status` (
    `name` varchar(26) NOT NULL, 
    PRIMARY KEY (`name`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

INSERT INTO `client_status` (`name`) VALUES 
('affiliate'), 
('customer'), 
('demo'), 
('disabled'), 
('reseller'); 

Come si può vedere 3 delle 4 tabelle usano il loro 'nome' come chiave primaria. So che questi saranno sempre unici. In 2 dei casi (le tabelle * _status) utilizzo fondamentalmente un sostituto dinamico per ENUM, poiché le opzioni di stato potrebbero cambiare in futuro, e per la tabella 'sito web', so che il 'nome' del sito web sarà sempre essere unico.

Mi chiedo se questa è una logica valida, eliminando gli ID di una tabella quando so che il nome sarà sempre un identificatore univoco o una ricetta per il disastro? Non sono un DBA esperto, quindi qualsiasi feedback, critica, ecc. Sarebbe estremamente utile.

Grazie per aver trovato il tempo di leggere questo!

+12

Benvenuti in una guerra santa che è stata condotta da prima che tu nascessi. :) –

+3

Su una nota più seria, una cosa che dovrete considerare è l'impatto di una colonna VARCHAR in un indice. Neanche io sono un DBA esperto, quindi non conosco la risposta. Ma consiglio vivamente di riempire una tabella di test, utilizzando un VARCHAR per una chiave primaria, con alcuni milioni di record e facendo alcuni test. Quindi fai lo stesso con INT come chiave primaria e vedi cosa succede. –

+0

Jordan ha ragione nel preoccuparsi di una chiave "ampia" come un varchar (126). Vedi la mia risposta qui sotto. – BradC

risposta

16

Ci sono 2 motivi vorrei sempre aggiungere un numero ID a una tabella di ricerca/ENUM:

  1. Se si fa riferimento un unico tavolo colonna con il nome allora si può essere meglio servita utilizzando un vincolo
  2. Cosa succede se si desidera rinominare una delle voci client_status? per esempio. se si desidera modificare il nome da "affiliato" a "utente affiliato", è necessario aggiornare la tabella client che non dovrebbe essere necessaria. Il numero ID funge da riferimento e il nome è la descrizione.

Nella tabella del sito Web, se si è certi che il nome sarà univoco, è opportuno utilizzarlo come chiave primaria. Personalmente, assegnerei comunque un ID numerico in quanto riduce lo spazio utilizzato nelle tabelle di chiavi esterne e trovo che sia più facile da gestire.

MODIFICA: Come indicato sopra, si verificheranno dei problemi se il nome del sito Web viene rinominato. Rendendo questa chiave primaria, sarà molto difficile, se non impossibile, modificarla in un secondo momento.

+3

Il numero due sopra riportato è la migliore ragione per non utilizzare il nome come chiave primaria. –

+0

Buona risposta. Ecco un motivo in più: L'operazione di confronto delle stringhe richiede molto più tempo rispetto al confronto numerico. –

+1

Le chiavi primarie non devono MAI trasportare dati utilizzabili fuori dal database.Cioè, l'unica ragione per cui le chiavi devono essere utilizzate è fare riferimento ai record nel database, non per presentare il valore delle chiavi all'utente. Il motivo è che le chiavi sono _immutabili_ e i dati non lo sono. Nome, genere, età, tipo di dipendente, numero di dipendenti, ecc. Sono tutti modificabili e non dovrebbero mai essere usati come chiave. Invece di utilizzare una sequenza bigint (o qualsiasi altra cosa), c'è un argomento per l'utilizzo di un guid: http://bit.ly/gSIkOG – BryanH

12

Quando si effettua il naturale PRIMARY KEY, accertarsi che la propria unicità sia sotto il proprio controllo.

Se sei assolutamente sicuro di non avere mai una violazione di unicità, allora è OK utilizzare questi valori come PRIMARY KEY.

Dato che website_status e client_status sembrano essere generati e utilizzati da te e solo da te, è accettabile utilizzarli come PRIMARY KEY, sebbene avere una chiave lunga possa influire sulle prestazioni.

website nome sembra essere sotto controllo del mondo esterno, è per questo che farei un campo semplice. Cosa succede se vogliono rinominare il loro website?

I controesempi sarebbero i codici SSN e ZIP: non siete voi a generarli e non vi è alcuna garanzia che non vengano mai duplicati.

+1

Ho sentito che due persone hanno assegnato lo stesso SSN. Non dovrebbe accadere, ma è successo comunque, a meno che non sia disinformato. Un problema più grande sono le persone che utilizzano SSN "contraffatti" per ottenere un impiego. Ora la tua origine dati è corrotta, anche se l'Amministrazione della sicurezza sociale sta gestendo correttamente gli SSN. –

+1

@Walter: il mio punto preciso. Le SSN NON devono essere utilizzate come chiavi primarie – Quassnoi

+1

Mi piace il fatto che l'unicità sia sotto il tuo controllo! Abbiamo persino avuto presunti campi ID interger unici dai clienti che si sono rivelati non unici, quando li hanno riutilizzati o sono andati a un nuovo sistema. – HLGEM

0

Ecco alcuni punti devono essere considerati prima di decidere le chiavi nella tabella

  • tasto numerico è più adatto quando si riferimenti uso (chiavi esterne), dal momento che che non utilizzano le chiavi esterne, ma ok in tua caso per utilizzare una chiave non numerica.

  • La chiave non numerica utilizza più spazio dei tasti numerici , può ridurre le prestazioni di .

  • tasti numerici rendono db sembrano più semplici da capire (si può facilmente sapere non di righe appena, cercando in ultima fila)
+5

Il valore del tasto numerico per l'ultima riga non è una buona indicazione del numero totale di righe nel database, a meno che non si sia MAI eliminato un record nel proprio database. –

3

Oltre a tutti gli altri punti eccellenti che sono già stati fatti, aggiungerei ancora una parola di cautela rispetto all'utilizzo di campi di grandi dimensioni come chiavi di clustering in SQL Server (se non si utilizza SQL Server, questo probabilmente non si applica a voi).

Aggiungo questo perché in SQL Server, la chiave primaria su un tavolo di default è anche la chiave di clustering (puoi cambiarla, se vuoi e conoscerla, ma la maggior parte dei casi, non è fatta) .

La chiave di clustering che determina l'ordine fisico della tabella di SQL Server viene inoltre aggiunta a ogni singolo indice non cluster su tale tabella. Se hai solo poche centinaia o poche migliaia di righe e uno o due indici, non è un grosso problema. Ma se si dispone di tabelle veramente grandi con milioni di righe e potenzialmente di molti indici per velocizzare le query, ciò causerà sprechi inutilmente di molto spazio su disco e memoria del server.

E.g. se la tua tabella ha 10 milioni di righe, 10 indici non in cluster e la tua chiave di clustering è 26 byte invece di 4 (per INT), stai sprecando 10 milioni. da 10 a 22 byte per un totale di 2,2 miliardi di byte (o circa 2,2 GBytes) - non sono più le noccioline!

Ancora: questo si applica solo a SQL Server e solo se si dispone di tabelle molto grandi con molti indici non cluster.

Marc

+0

"puoi cambiarlo, se vuoi e conoscerlo, ma la maggior parte dei casi, non è fatto" - Solo se la persona che progetta il database non è un buon progettista di database. Un progettista esperto e qualificato terrà conto di molte cose prima di decidere cosa utilizzare nell'indice cluster. –

+1

Tom H: sì, certo - ma la maggior parte degli sviluppatori di app non sono allo stesso tempo grandi progettisti di database, nella mia personale esperienza. Molti sviluppatori di app considerano il database un "dumb storage dump" in cui possono semplicemente trascinare i loro oggetti - e non pensano abbastanza alla progettazione del database. –

1

Personalmente, penso che sarà eseguito nei guai con questa idea. Man mano che ci si ritrova in relazioni più parentali, si finisce con un'enorme quantità di lavoro quando i nomi cambiano (come sempre prima o poi). Ci può essere un grande successo nelle prestazioni quando si deve aggiornare una tabella figlia che ha migliaia di righe quando il nome del sito cambia. E devi pianificare come fare per assicurarti che quei cambiamenti avvengano. Altrimenti, il nome del sito web cambia (oops lasciamo che il nome scada e qualcun altro lo abbia acquistato). Si interrompe a causa del vincolo di chiave esterna o è necessario inserirlo in modo automatico (aggiornamento a cascata) per propagare la modifica attraverso il sistema. Se si utilizzano gli aggiornamenti a cascata, è possibile interrompere improvvisamente il sistema mentre viene elaborata una chage grande. Questo non è considerato una buona cosa. È davvero più efficace ed efficiente utilizzare gli ID per le relazioni e quindi inserire indici univoci nel campo del nome per garantire che rimangano unici. La progettazione del database deve considerare la manutenzione dell'integrità dei dati e il modo in cui ciò influirà sulle prestazioni.

Un'altra cosa da considerare è che i nomi dei siti web tendono ad essere più lunghi di pochi caratteri. Ciò significa che la differenza di prestazioni tra l'utilizzo di un campo id per i join e il nome per i join potrebbe essere piuttosto significativo.Devi pensare a queste cose in fase di progettazione poiché è troppo tardi per passare a un ID quando hai un sistema di produzione con milioni di record che è scaduto e la correzione è di ristrutturare completamente il databse e riscrivere tutto il codice SQL codice. Non qualcosa che puoi risolvere in quindici minuti per far funzionare di nuovo il sito.

1

Questa sembra davvero una pessima idea. Cosa succede se è necessario modificare il valore dell'enum? L'idea è di renderlo un database relazionale e non un insieme di file flat. A questo punto, perché la tabella client_status? Inoltre, se si utilizzano i dati in un'applicazione, utilizzando un tipo come GUID o INT, è possibile convalidare il tipo ed evitare dati non validi (nella misura in cui convalidano il tipo). Quindi, è un'altra delle molte linee per scoraggiare l'hacking.

8

Kimberly Tripp ha un eccellente serie di articoli di blog (GUIDs as PRIMARY KEYs and/or the clustering key e The Clustered Index Debate Continues) sul tema della creazione di indici cluster, e la scelta della chiave primaria (questioni legate, ma non sempre esattamente la stessa cosa). La sua raccomandazione è che una chiave di indice/primaria cluster dovrebbe essere:

  1. unico (altrimenti inutile come chiave)
  2. Narrow (la chiave è utilizzata in tutti gli indici non cluster, e in valuta estera relazioni tasto)
  3. Static (che non si desidera avere a cambiare tutti i record correlati)
  4. sempre crescente (in modo sempre nuovi record vengono aggiunti alla fine della tabella, e non devono essere inseriti al centro)

Utilizzare "Nome" come chiave, mentre sembra soddisfare il numero 1, non soddisfa NESSUNO degli altri tre.

Anche per la vostra tavola "di ricerca", che cosa se il vostro capo decide di cambiare tutto affiliato s per socio s, invece? Dovrai modificare tutte le righe nel database che usano questo valore.

Dal punto di vista delle prestazioni, sono probabilmente più preoccupato che una chiave sia stretta. Se il nome del tuo sito Web è in realtà un URL lungo, questo potrebbe davvero ingrossare la dimensione di qualsiasi indice non cluster e tutte le tabelle che lo utilizzano come chiave esterna.

1

Direi che un database che è resistente alla corruzione, anche se funziona un po 'più lentamente, è meglio di uno che non è ’ t.

In generale, le chiavi surrogate (come identificatori numerici arbitrari) compromettono l'integrità del database. Le chiavi primarie sono il modo principale per identificare le righe nel database; se i valori della chiave primaria non sono significativi, il vincolo non è significativo. Pertanto anche eventuali chiavi esterne che fanno riferimento a chiavi primarie surrogate sono sospette. Ogni volta che è necessario recuperare, aggiornare o eliminare singole righe (ed essere garantito che ne influenza solo una), la chiave primaria (o un'altra chiave candidata) è ciò che è necessario utilizzare; dover lavorare su ciò che un valore di chiave surrogata è quando c'è una chiave alternativa significativo è un passo ridondante e potenzialmente pericolosa per utenti e applicazioni.

Anche se significa usando una chiave composta per garantire l'univocità, Vorrei sostenere con un significativo, set naturale di attributi come chiave primaria, quando possibile. Se è necessario comunque registrare gli attributi, perché aggiungerne un altro?Detto questo, le chiavi surrogate vanno bene quando non esiste una chiave naturale, stabile, concisa, garantita per essere unica (ad esempio per le persone).

Si potrebbe anche considerare l'utilizzo della compressione della chiave dell'indice, se il DBMS lo supporta. Questo può essere molto efficace, specialmente per gli indici su chiavi composite (si pensi alle strutture dati trie), e specialmente se gli attributi meno selettivi possono apparire per primi nell'indice.

2

"Se sei assolutamente sicuro di non avere mai una violazione di unicità, allora è OK utilizzare questi valori come PRIMARY KEY."

Se sei assolutamente sicuro di non avere mai una violazione di unicità, non preoccuparti di definire la chiave.

1

Penso di essere d'accordo con cheduardo. Sono passati 25 anni da quando ho seguito un corso di progettazione di database, ma ricordo che mi è stato detto che i motori di database possono gestire e caricare in modo più efficiente gli indici che usano le chiavi dei caratteri. I commenti sul database che devono aggiornare migliaia di record quando una chiave viene cambiata e su tutto lo spazio aggiunto occupato dalle chiavi più lunghe e quindi devono essere trasferiti attraverso i sistemi, si presuppone che la chiave sia effettivamente memorizzata nei record e che non deve essere trasferito attraverso i sistemi comunque. Se si crea un indice su una colonna (s) di una tabella, non penso che il valore sia memorizzato nei record della tabella (a meno che non si imposti alcune opzioni per farlo).

Se si dispone di una chiave naturale per una tabella, anche se è cambiata occasionalmente, la creazione di un'altra chiave crea una ridondanza che potrebbe causare problemi di integrità dei dati e crea effettivamente ancora più informazioni che devono essere memorizzate e trasferite tra i sistemi. Lavoro per un team che ha deciso di archiviare le impostazioni dell'applicazione locale nel database. Hanno una colonna Identity per ogni impostazione, un nome di sezione, un nome chiave e un valore chiave. Hanno una procedura memorizzata (un'altra guerra santa) per salvare un'impostazione che assicura che non appaia due volte. Devo ancora trovare un caso in cui vorrei usare l'ID di una impostazione. Ho, tuttavia, finito con più record con la stessa sezione e il nome della chiave che ha causato il fallimento della mia applicazione. E sì, so che avrebbe potuto essere evitato definendo un vincolo sulle colonne.

+0

Non vi è alcun vantaggio in termini di efficienza per avere le chiavi 'char' in contrapposizione a quelle numeriche (né c'è necessariamente una penalità). I valori della chiave * sono * memorizzati nei "record" - tutti i valori sono (eccetto le colonne di valore speciale, ma non li indicherete comunque). Gli indici * duplicano * i dati che stanno coprendo. Questi campi non vengono mai rimossi dai dati solo perché sono indicizzati. –

+0

Sospetto che i dati siano memorizzati nella tabella o no è un'opzione. "Il livello inferiore, o foglia, dell'indice cluster contiene le righe di dati effettive della tabella: una tabella o vista è consentita un indice cluster alla volta." Ricordo vagamente dalla mia lezione di teoria del database che è possibile memorizzare i valori per le colonne indicizzate nell'indice, quindi questi dati non sono stati duplicati per ogni riga della tabella. –

Problemi correlati