È buona norma disporre sempre di una chiave primaria intera con incremento automatico?


191

Nei miei database, tendo a prendere l'abitudine di avere una chiave primaria intera a incremento automatico con il nome id per ogni tabella che creo in modo da avere una ricerca univoca per ogni riga particolare.

È considerata una cattiva idea? Ci sono degli svantaggi nel farlo in questo modo? A volte avrò più indici come id, profile_id, subscriptionsdove si idtrova l'identificatore univoco, i profile_idcollegamenti allo straniero iddi aProfile tabella, ecc.

O ci sono scenari in cui non si desidera aggiungere un tale campo?


61
Dai un'occhiata al problema del serbatoio tedesco per un esempio in cui un semplice identificatore auto-incrementante è un problema. Naturalmente questo è importante solo se stai usando i tuoi ID in pubblico.
Bergi,

24
@ArukaJ Il punto è che perde alcune informazioni sul sistema. Ad esempio, supponiamo che il database contenga post scritti dall'utente, ognuno dei quali ottiene un ID sequenziale. Supponi di scrivere quattro post, ognuno dei quali ottiene un ID: alle 4 (20), alle 5 (25), alle 20 (100) e alle 21 (200). Guardando gli ID, puoi vedere che sono stati aggiunti solo 5 post tra le 4 e le 5, mentre 100 sono stati aggiunti tra le 20 e le 21. Se stavi cercando di scegliere il tempo per un attacco di negazione del servizio, queste potrebbero essere informazioni preziose.
Joshua Taylor,

29
Per tutti coloro che si lamentano del "problema dei carri armati tedeschi" .... se l'unica cosa che impedisce a qualcuno di accedere ai dati non dovrebbe essere una chiave nel tuo URL ... hai problemi più grandi di GUID contro Auto INT.
Matthew Whited,

11
@MatthewWhited Non si tratta solo di scambiare parametri in un URL. Supponiamo di utilizzare un sito e di creare l'asset 100 alla volta te l'asset 120 alla volta t + 60. Se riesci a vedere entrambi questi ID (100 e 120) in forma non offuscata, ora conosci il numero totale di risorse esistenti e approssimativamente la velocità con cui vengono creati. Questa è una perdita di informazioni. Questo non è puramente ipotetico.
Chris Hayes,

15
"È buona pratica sempre ..." No.
brian_o,

Risposte:


137

Non è mai una cattiva idea avere un identificatore di riga univoco garantito. Immagino che non dovrei dire mai - ma andiamo con la stragrande maggioranza delle volte è una buona idea.

I potenziali svantaggi teorici includono un indice aggiuntivo da mantenere e uno spazio di archiviazione aggiuntivo utilizzato. Per me non è mai stato un motivo sufficiente per non usarne uno.


11
Questo è ciò che faccio. Molte persone usano "id" o "tablename_id" (come user_id). L'argomento non è in genere se la colonna è necessaria, ma in che modo denominarla.
GrandmasterB,

103
Personalmente penso che il nome della tabella dovrebbe implicare il resto. TableName.idal contrario TableName.TableName_id, perché a che altro si idriferirà? Se ho un altro campo ID nella tabella, lo inserirò come prefisso con un nome di tabella se si riferisce ad un'altra tabella
AJJ

10
@ArukaJ hai menzionato che stai usando SQLite. Questo è in realtà un po 'un caso speciale, in quanto rende sempre una colonna del genere "sotto il cofano". Quindi non stai nemmeno usando alcuno spazio extra perché ne ottieni uno, che tu lo voglia o no. Inoltre, il rowid di SQLite è sempre un numero intero a 64 bit. Se la mia comprensione è corretta, se si definisce una riga auto-incrementante, sarà un alias del rowid interno. Quindi potresti sempre farlo! Vedi sqlite.org/autoinc.html
GrandmasterB

9
L'unica eccezione che mi viene in mente è se si dispone di un identificatore univoco che viene generato in qualche altro modo, nel qual caso dovrebbe essere la chiave primaria e un ID auto-incrementante è ridondante.
HamHamJ,

4
@GrandmasterB: la versione corrente di SQLite consente di creare WITHOUT ROWIDtabelle (con un esplicito PRIMARY KEY) come ottimizzazione. In caso contrario, una INTEGER PRIMARY KEYcolonna è un alias per il rowid.
dan04,

92

Non sono d'accordo con tutte le risposte prima. Ci sono molti motivi per cui è una cattiva idea aggiungere un campo di incremento automatico in tutte le tabelle.

Se hai una tabella in cui non ci sono chiavi ovvie, un campo di incremento automatico sembra una buona idea. Dopo tutto, non vuoi select * from blog where body = '[10000 character string]'. Preferirestiselect * from blog where id = 42 . Direi che nella maggior parte di questi casi, ciò che vuoi davvero è un identificatore univoco; non un identificatore univoco sequenziale. Probabilmente si desidera utilizzare invece un identificatore univoco universale.

Ci sono funzioni nella maggior parte dei database per generare identificatori univoci casuali ( uuidin mysql, postgres.newid In mssql). Ciò consente di generare dati in più database, su macchine diverse, in qualsiasi momento, senza alcuna connessione di rete tra loro, e di unire ancora i dati con zero conflitti. Ciò consente di configurare più facilmente server multipli e persino data center, come ad esempio con microservizi.

Questo evita anche che gli aggressori indovinino gli URL alle pagine a cui non dovrebbero avere accesso. Se c'è un https://example.com/user/1263probabilmente c'è un https://example.com/user/1262 . Ciò potrebbe consentire l'automazione di un exploit di sicurezza nella pagina del profilo utente.

Ci sono anche molti casi in cui una colonna uuid è inutile o addirittura dannosa. Diciamo che hai un social network. C'è un userstavolo e un friendstavolo. La tabella degli amici contiene due colonne userid e un campo di incremento automatico. Vuoi 3essere amico di 5, quindi inseriscilo 3,5nel database. Il database aggiunge un ID di incremento automatico e memorizza 1,3,5. In qualche modo, l'utente fa di nuovo 3clic sul pulsante "Aggiungi amico". Si inserisce 3,5nuovamente nel database, il database aggiunge un ID di incremento automatico e inserisce 2,3,5. Ma ora 3e 5siamo amici l'uno con l'altro due volte! Questo è uno spreco di spazio, e se ci pensate, lo è anche la colonna di auto-incremento. Tutto ciò che serve per vedere se aebsono amici è selezionare per la riga con questi due valori. Sono, insieme, un identificatore di riga univoco. (Probabilmente vorrai scrivere qualche logica per assicurarti 3,5e 5,3sono deduplicati.)

Ci sono ancora casi in cui gli ID sequenziali possono essere utili, come quando si costruisce un accorciatore di url, ma soprattutto (e anche con l'accorciatore di url) un ID univoco generato casualmente è ciò che si desidera realmente utilizzare.

TL; DR: utilizzare gli UUID anziché l'auto-incremento, se non si dispone già di un modo univoco per identificare ciascuna riga.


26
Il problema con gli UUID è che occupano troppo spazio per la maggior parte delle tabelle. Utilizzare l'identificatore univoco corretto per ciascuna tabella.
Stephen,

49
L'intero paragrafo sull'unicità è controverso: l'unicità può essere applicata, con o senza chiave primaria. Inoltre, gli UUID sono migliori dal punto di vista teorico, ma terribili da usare quando si esegue il debug / eseguono attività DBA o si fa altrimenti qualsiasi cosa che non "resista agli attacchi".

11
Un altro scenario in cui gli UUID sono migliori: l'implementazione di un'operazione PUT idempotente, in modo da poter riprovare in sicurezza le richieste senza introdurre righe duplicate.
yurez,

21
Sul punto "Indovinello di URL", avere un ID univoco (sequenziale o altro) non implica esporre tale ID agli utenti dell'applicazione.
Dave Sherohman,

7
Puramente dal punto di vista del database, questa risposta è completamente sbagliata. L'uso di UUID invece di interi con incremento automatico aumenta gli indici in modo troppo rapido e influisce negativamente sulle prestazioni e sul consumo di memoria. Se stai parlando dal punto di vista del servizio web o dell'app web, dovrebbe esserci comunque un livello tra il database e il front-end. Qualsiasi altra cosa è cattiva progettazione. L'uso dei dati come chiave primaria è anche peggio. Le chiavi primarie dovrebbero essere utilizzate solo sul livello dati, da nessun'altra parte.
Drunken Code Monkey

60

Le chiavi autoincementali presentano principalmente vantaggi.

Ma alcuni possibili inconvenienti potrebbero essere:

  • Se si dispone di una chiave aziendale, è necessario aggiungere un indice univoco anche su quella colonna / e per applicare le regole aziendali.
  • Quando si trasferiscono dati tra due database, in particolare quando i dati si trovano in più di una tabella (ovvero master / dettaglio), non è semplice poiché le sequenze non sono sincronizzate tra i database e sarà necessario creare prima una tabella di equivalenza utilizzando chiave di business come corrispondenza per sapere quale ID del database di origine corrisponde a quale ID nel database di destinazione. Tuttavia, ciò non dovrebbe costituire un problema durante il trasferimento di dati da / a tabelle isolate.
  • Molte aziende dispongono di strumenti di reporting ad hoc, grafici, point-and-click, drag-and-drop. Poiché gli ID autoincrementali non hanno senso, questo tipo di utenti troverà difficile dare un senso ai dati al di fuori di "l'app".
  • Se modifichi accidentalmente la chiave aziendale, è probabile che non recupererai mai quella riga perché non hai più qualcosa che gli umani possano identificarla. Ciò ha causato un errore nella piattaforma BitCoin una volta .
  • Alcuni designer aggiungono un ID a una tabella di join tra due tabelle, quando il PK dovrebbe essere semplicemente composto da due ID esterni. Ovviamente se la tabella di join si trova tra tre o più tabelle, allora ha senso un ID autoincrementale, ma è necessario aggiungere una chiave univoca quando si applica alla combinazione di FK per far rispettare le regole aziendali.

Ecco una sezione dell'articolo di Wikipedia sugli svantaggi delle chiavi surrogate.


13
Incolpare il difetto mt.gox sui tasti surrogati sembra piuttosto dubbio. Il problema era che includevano tutti i campi nella loro chiave composta, anche i campi mutabili / malleabili.
Codici InCos

6
Uno svantaggio "sociale" dell'uso delle chiavi di incremento automatico è che a volte "l'azienda" presume che non ci debbano mai essere spazi vuoti e richiede di sapere cosa è successo alle righe mancanti che si verificano quando si verifica un inserimento non riuscito (rollback della transazione).
Rick Ryker,

4
Un altro svantaggio è che se il sistema diventa così grande da frammentare il database, non è più possibile utilizzare l'autoincremento per produrre una chiave univoca a livello globale. Quando arrivi a quel punto, potresti avere un sacco di codice che si basa su tale presupposto. Esistono altri modi per produrre un identificatore univoco che continuerà a funzionare se il database è suddiviso.
Kasperd,

1
@Voo Non è garantito che il database scelto lo supporti. E provare a implementarlo a un livello superiore rispetto al database stesso significa perdere alcune delle garanzie che SQL ti darebbe. Infine, qualsiasi assegnazione centralizzata di ID aumenterà la latenza se si dispone di un sistema distribuito.
Kasperd,

1
@Voo Ovviamente, indipendentemente dalla scala del sistema, non si dovrebbero fare troppe ipotesi sulla natura degli ID autoincrementati. Se si dispone di un solo database, vengono assegnati in ordine, ma non esiste alcuna garanzia che vengano assegnati in ordine. E ci può essere gap nella sequenza perché non tutte le transazioni sono impegnate.
Kasperd,

20

Per essere al contrario, No, NON è necessario disporre sempre di un AutoKc numerico.

Se analizzi attentamente i tuoi dati, spesso identifichi le chiavi naturali nei dati. Questo è spesso il caso in cui i dati hanno un significato intrinseco per l'azienda. A volte i PK sono artefatti di sistemi antichi che gli utenti aziendali utilizzano come seconda lingua per descrivere gli attributi del loro sistema. Per esempio, ho visto i numeri VIN del veicolo utilizzati come chiave primaria di una tabella "Veicolo" in un sistema di gestione della flotta.

Comunque abbia avuto origine, SE hai già un identificatore univoco, usalo. Non creare una seconda chiave primaria insignificante; è dispendioso e può causare errori.

A volte è possibile utilizzare un PK AutoInc per generare un valore significativo per il cliente, ad esempio Numeri di polizza. Impostare il valore iniziale su qualcosa di sensato e applicare le regole di business sui principali zeri ecc. Questo è probabilmente un approccio "migliore di entrambi i mondi".

Quando si dispone di un numero limitato di valori relativamente statici, utilizzare i valori che hanno senso per l'utente del sistema. Perché utilizzare 1,2,3 quando è possibile utilizzare L, C, H dove L, H e C rappresentano la vita, l'auto e la casa in un contesto di "tipo di polizza" assicurativo o, tornando all'esempio VIN, che ne dici di usare "TO "per Toyota? Tutte le auto Toyata hanno un VIN che inizia con "TO" È una cosa in meno che gli utenti devono ricordare, rende meno probabile l'introduzione di errori di programmazione e degli utenti e può anche essere un surrogato utilizzabile per una descrizione completa nei report di gestione che semplifica i report da scrivere e forse più veloce da generare.

Un ulteriore sviluppo di questo è probabilmente "un ponte troppo lontano" e generalmente non lo consiglio, ma lo sto includendo per completezza e potresti trovare un buon uso per questo. Cioè, utilizzare la descrizione come chiave primaria. Per i dati che cambiano rapidamente questo è un abominio. Per dati molto statici riportati su All The Time , forse no. Ne ho appena parlato, quindi è seduto lì come una possibilità.

Faccio uso di AutoInc PK, mi impegno solo il cervello e cerco prima alternative migliori. L'arte della progettazione di database sta facendo qualcosa di significativo che può essere interrogato rapidamente. Avere troppi join impedisce questo.

MODIFICA Un altro caso cruciale in cui non è necessario un PK autogenerato è il caso di tabelle che rappresentano l'intersezione di altre due tabelle. Per attenersi all'analogia di Car, A Car ha 0..n accessori, ogni accessorio può essere trovato su molte auto. Quindi, per rappresentarlo, crei una tabella Car_Accessory contenente i PK di auto e accessori e altre informazioni rilevanti sulle date del link, ecc.

Ciò di cui non hai bisogno (di solito) è un PK AutoInc su questa tabella: si accederà solo tramite l'auto "dimmi quali accessori ci sono su questa auto" o dall'Accessorio "dimmi quali auto hanno questo accessorio"


4
> Tutte le auto Toyata hanno un VIN che inizia "TO" Questo non è vero. Iniziano con "JT" se realizzati in Giappone. I Toyotas americani hanno VIN completamente diversi en.wikibooks.org/wiki/…
Monty Harder

17
Don't create a second, meaningless primary key; it's wasteful and may cause errors.Tuttavia, se il modo in cui si stabilisce l'unicità per un record è una combinazione di 6 colonne, unirsi a tutte e 6 tutte le volte è molto soggetto a errori. I dati hanno naturalmente un PK ma è meglio usare una idcolonna e un vincolo univoco su quelle 6 colonne.
Brad

14
Ammetto che alcuni di questi suggerimenti mi spingono un po 'oltre. Sì, essere pragmatici va bene, ma non posso contare con quale frequenza qualcuno ha giurato nella vita del suo primogenito che alcuni attributi fuori dal dominio rimarranno unici per il resto dei giorni. Bene, di solito ha funzionato bene fino alla seconda settimana dopo essere andato in diretta, quando sono arrivati ​​i primi duplicati. ;) L'uso di una "descrizione" come PK è solo molto lontano.
AnoE

2
@Monty, mia cattiva, hai ragione. Memoria fallibile, sono passati 20 anni da quando ho progettato i sistemi di gestione della flotta. No, il VIN non era la chiave primaria :) Ho usato un AutoInc Asset_ID IIRC che porta a qualcosa che ho dimenticato. Tabelle che sono i linker per le relazioni molti-a-molti in cui si collega, per esempio, un'auto all'accessorio (es. Tetto apribile) Molte auto hanno molti accessori quindi è necessario un tavolo "Car_Accessory" che contiene Car_ID e Accessory_ID ma NON ha assolutamente bisogno di Car_Accesory_ID come un PK AutoInc.
mcottle,

7
È davvero sorprendente quante poche "chiavi naturali" VERAMENTE immutabili ci siano. Del SSN? No, possono cambiare. È raro, ma può succedere. Nomi utente? No. Alla fine qualcuno avrà un valido motivo commerciale per cambiare. VIN è spesso un esempio da manuale, ma non ce ne sono molti altri. Anche gli indirizzi di casa possono cambiare, date le modifiche ai nomi delle strade.
Erik Funkenbusch,

12

Molti tavoli hanno già un ID unico naturale. Non aggiungere un'altra colonna ID univoca (incremento automatico o altro) a queste tabelle. Utilizzare invece l'id unico naturale. Se aggiungi un altro ID univoco, essenzialmente hai una ridondanza (duplicazione o dipendenza) nei tuoi dati. Questo va contro i principi della normalizzazione. Un ID univoco dipende dall'altro per la precisione. Ciò significa che devono essere perfettamente sincronizzati in ogni momento in ogni sistema che gestisce queste righe. È solo un'altra fragilità nell'integrità dei dati che non vuoi davvero gestire e convalidare a lungo termine.

La maggior parte delle tabelle al giorno d'oggi non ha davvero bisogno del potenziamento delle prestazioni molto minore che darebbe una colonna id univoca aggiuntiva (e talvolta riduce anche le prestazioni). Come regola generale nell'IT, evitare la ridondanza come la peste! Resistilo ovunque ti venga suggerito. È un anatema. E attenzione alla citazione. Tutto dovrebbe essere il più semplice possibile, ma non più semplice. Non avere due ID univoci in cui uno sarà sufficiente, anche se quello naturale sembra meno ordinato.


3
Non dovresti usare ID "naturali" come chiavi primarie se sono assolutamente garantiti che non cambieranno mai? Ad esempio, non dovresti usare un numero di patente di guida come chiave primaria, perché se una persona ottiene una nuova patente di guida, dovrai aggiornare non solo quella tabella ma tutte le tabelle con chiavi esterne che fanno riferimento a essa!
ekolis,

1
Esistono diversi motivi per cui il numero di patente di guida non si qualifica come ID univoco naturale. Innanzitutto alcuni di essi derivano da altri dati, come la data e il nome di nascita. Non sono garantiti unici in tutti gli stati. E per fare il tuo esempio, quando a una persona viene rilasciata una licenza con lo stesso numero, ma forse una scadenza estesa, cosa succede allora? Hanno una licenza diversa con lo stesso numero. Un id naturale deve ancora soddisfare le proprietà di base di una chiave primaria. Il numero di patente di guida (almeno negli Stati Uniti) presenta alcune carenze al riguardo.
Brad Thomas,

1
OK, immagino di aver frainteso la definizione di ID naturale allora; Ho pensato che fosse semplicemente un ID definito dalle regole aziendali, indipendentemente dal fatto che fosse effettivamente garantito come immutabile.
ekolis,

10

Sui sistemi più grandi, ID è un booster di coerenza, usalo quasi ovunque. In questo contesto, le singole chiavi primarie NON sono raccomandate, sono costose nella linea di fondo (leggi perché).

Ogni regola ha un'eccezione, quindi potrebbe non essere necessario l'ID di incremento automatico intero sulle tabelle di gestione temporanea utilizzate per l'esportazione / importazione e su tabelle unidirezionali simili o tabelle temporanee. Preferiresti anche i GUID invece degli ID sui sistemi distribuiti.

Molte risposte qui suggeriscono che dovrebbe essere presa la chiave unica esistente. Bene anche se ha 150 caratteri? Io non la penso così.

Ora il mio punto principale:

Sembra che gli oppositori dell'ID intero di autoincremento stiano parlando di piccoli database con un massimo di 20 tabelle. Lì possono permettersi un approccio individuale a ciascun tavolo.

MA una volta che hai un ERP con oltre 400 tabelle, con ID di incremento automatico intero ovunque (tranne i casi menzionati sopra) ha molto senso. Non si fa affidamento su altri campi univoci anche se sono presenti e protetti per unicità.

  • Approfittate di convenzioni universali per risparmiare tempo, risparmiare sforzo e facili da ricordare.
  • Nella maggior parte dei casi JOINtabelle, senza bisogno di controllare quali sono le chiavi.
  • Puoi avere routine di codice universali che funzionano con la colonna di incremento automatico dei numeri interi.
  • È possibile estendere il sistema con nuove tabelle o plug-in utente non previsti prima semplicemente facendo riferimento agli ID delle tabelle esistenti. Sono già lì dall'inizio, senza costi aggiuntivi per aggiungerli.

Sui sistemi più grandi, può valere la pena ignorare i vantaggi minori di quelle singole chiavi primarie e utilizzare costantemente l'ID di incremento automatico intero nella maggior parte dei casi. L'uso di campi univoci esistenti come chiavi primarie sta forse salvando alcuni byte per record, ma i tempi di archiviazione o indicizzazione aggiuntivi non rappresentano un problema nei motori di database di oggi. In realtà stai perdendo molto più denaro e risorse sul tempo perso dagli sviluppatori / manutentori. Il software di oggi dovrebbe essere ottimizzato per il tempo e lo sforzo dei programmatori: quale approccio con ID coerenti soddisfa molto meglio.


Per esperienza personale, sono pienamente d'accordo con la seconda metà della tua risposta. Avrai bisogno di chiavi univoche a livello globale, molto meno spesso di quanto avrai bisogno di indici veloci e compatti. Se ne hai bisogno, crea una tabella GlobalEntities con un ID generato automaticamente e una colonna UUID. Quindi aggiungere una chiave esterna ExGlobalEntityId alla tabella Clienti, ad esempio. Oppure usa un hash di alcuni dei valori.
Drunken Code Monkey,

8

Non è buona pratica progettare in modo superfluo. Vale a dire - non è buona norma disporre sempre di una chiave primaria di incremento automatico quando non è necessaria.

Vediamo un esempio in cui non è necessario.

Hai una tabella per gli articoli: questa ha una chiave primaria int ide una colonna varchar denominata title.

Hai anche una tabella piena di categorie di articoli - in idchiave primaria, varchar name.

Una riga nella tabella Articoli ha un numero iddi 5 e un title "Come cucinare l'oca con il burro". Desideri collegare l'articolo con le seguenti righe nella tabella Categorie: "Fowl" ( id : 20), "Goose" ( id : 12), "Cooking" ( id : 2), "Butter" (id: 9) .

Ora hai 2 tabelle: articoli e categorie. Come si crea la relazione tra i due?

Potresti avere una tabella con 3 colonne: id (chiave primaria), article_id (chiave esterna), category_id (chiave esterna). Ma ora hai qualcosa del tipo:

| id | a_id | c_id |
| 1 | 5 | 20 |
| 2 | 5 | 12 |
| 3 | 5 | 2 |

Una soluzione migliore è avere una chiave primaria composta da 2 colonne.

| a_id | c_id |
| 5 | 20 |
| 5 | 12 |
| 5 | 2 |

Questo può essere realizzato facendo:

create table articles_categories (
  article_id bigint,
  category_id bigint,
  primary key (article_id, category_id)
) engine=InnoDB;

Un altro motivo per non utilizzare un numero intero con incremento automatico è se si utilizzano UUID per la chiave primaria.

Gli UUID sono, per definizione, unici, il che compie la stessa cosa che usa gli interi univoci. Hanno anche i loro vantaggi (e contro) aggiuntivi rispetto agli interi. Ad esempio, con un UUID, sai che la stringa univoca a cui ti riferisci punta a un particolare set di dati; questo è utile nei casi in cui non si dispone di 1 database centrale o in cui le applicazioni hanno la possibilità di creare record di dati offline (quindi caricarli nel database in un secondo momento).

Alla fine, non devi pensare alle chiavi primarie come una cosa. Devi pensare a loro come alla funzione che svolgono. Perché hai bisogno delle chiavi primarie? Essere in grado di identificare in modo univoco insiemi di dati specifici da una tabella utilizzando un campo che non verrà modificato in futuro. Hai bisogno di una colonna specifica chiamata idper fare questo, o puoi basare questa identificazione univoca su altri dati (immutabili)?


7

O ci sono scenari in cui non si desidera aggiungere un tale campo?

Sicuro.

Prima di tutto, ci sono database che non hanno autoincrementi (ad esempio Oracle, che certamente non è uno dei contendenti più piccoli in circolazione). Questa dovrebbe essere una prima indicazione del fatto che non a tutti piacciono o ne hanno bisogno.

Ancora più importante, pensa a cosa sia effettivamente l'ID : è una chiave primaria per i tuoi dati. Se hai una tabella con una chiave primaria diversa, non hai bisogno di un ID e non dovresti averne una. Ad esempio, una tabella (EMPLOYEE_ID, TEAM_ID)(in cui ciascun dipendente può far parte di più team contemporaneamente) ha una chiave primaria chiaramente definita costituita da quei due ID. L'aggiunta di una IDcolonna di incremento automatico , che è anche una chiave primaria per questa tabella, non avrebbe alcun senso. Ora stai trascinando in giro 2 chiavi primarie e la prima parola in "chiave primaria" dovrebbe darti un suggerimento che dovresti davvero averne solo una.


9
(Non è un utente Oracle a perdonare la domanda, ma) Oracle non usa Sequence nello stesso modo in cui altri usano Autoincrement / Identity? Dire che Oracle non ha un tipo di dati di Autoincrement in realtà è solo un argomento sematico?
Brad

Bene, quello era solo un piccolo punto; la parte principale è che un ID in esecuzione non è appropriato per ogni tabella, quindi abituarsi a schiaffeggiare un ID automatico su ogni singola tabella potrebbe non essere il più saggio.
AnoE

non ci sono due chiavi primarie, c'è solo una chiave primaria e tutto il resto sono chiamati chiavi candidate se possono servire anche come chiavi primarie ..
rahul tyagi

7

Di solito utilizzo una colonna "identità" (numero intero auto-incremennante) quando definisco nuove tabelle per dati "di lunga durata" (record che prevedo di inserire una volta e di conservare a tempo indeterminato anche se finiscono per "logicamente cancellati" impostando un campo bit ).

Ci sono alcune situazioni a cui riesco a pensare quando non vuoi usarle, la maggior parte delle quali si riducono a scenari in cui una tabella su un'istanza del DB non può essere la fonte autorevole per i nuovi valori ID:

  • Quando gli ID incrementali sarebbero troppe informazioni per un potenziale attaccante. L'uso di una colonna di identità per servizi di dati "rivolti al pubblico" ti rende vulnerabile al "problema dei carri armati tedeschi"; se esiste l'ID record 10234, è logico che esista il record 10233, 10232, ecc., almeno al record 10001, quindi è facile controllare i record 1001, 101 e 1 per capire dove è iniziata la colonna identità. I GUID V4 composti principalmente da dati casuali interrompono questo comportamento incrementale in base alla progettazione, quindi solo perché esiste un GUID, non esiste necessariamente un GUID creato aumentando o decrementando un byte del GUID, rendendo più difficile per un utente malintenzionato utilizzare un servizio indotto per il recupero di un singolo record come strumento di dump. Esistono altre misure di sicurezza che possono limitare meglio l'accesso, ma questo aiuta.
  • In M: M tabelle di riferimento incrociato. Questo è un tipo di dammi ma l'ho già visto fare prima. Se si dispone di una relazione molti-a-molti tra due tabelle nel database, la soluzione di riferimento è una tabella con riferimenti incrociati contenente colonne di chiavi esterne che fanno riferimento al PK di ciascuna tabella. Il PK di questa tabella dovrebbe essere virtualmente sempre una chiave composta delle due chiavi esterne, per ottenere il comportamento dell'indice incorporato e garantire l'univocità dei riferimenti.
  • Quando prevedi di inserire e cancellare in blocco su questa tabella molto. Probabilmente il più grande svantaggio delle colonne di identità è l'hoopla extra che devi affrontare quando esegui un inserimento di righe da un'altra tabella o query, in cui desideri mantenere i valori chiave della tabella originale. Devi attivare "inserimento identità" (comunque nel tuo DBMS), quindi assicurarti manualmente che le chiavi che stai inserendo siano univoche, quindi quando hai finito con l'importazione devi impostare il contatore identità nel i metadati della tabella al massimo valore presente. Se questa operazione si verifica molto su questa tabella, prendere in considerazione uno schema PK diverso.
  • Per tavoli distribuiti.Le colonne di identità funzionano perfettamente per database a istanza singola, coppie di failover e altri scenari in cui un'istanza di database è l'unica autorità sull'intero schema dati in qualsiasi momento. Tuttavia, c'è solo così grande che puoi andare e avere un computer abbastanza veloce. La replica o la distribuzione dei registri delle transazioni possono procurarti copie di sola lettura aggiuntive, ma esiste anche un limite alla scala di tale soluzione. Prima o poi avrai bisogno di due o più istanze del server che gestiscono gli inserti di dati e poi si sincronizzano tra loro. Quando si verifica tale situazione, è necessario un campo GUID anziché uno incrementale, poiché la maggior parte dei DBMS viene preconfigurata per utilizzare una parte dei GUID generati come identificatore specifico dell'istanza, quindi generare il resto dell'identificatore in modo casuale o in modo incrementale. In ogni caso,
  • Quando è necessario applicare l'univocità su più tabelle nel DB.È comune nei sistemi contabili, ad esempio, gestire la contabilità generale (con una riga per ogni credito o debito di ogni conto che si è mai verificato, quindi diventa molto grande molto rapidamente) come una sequenza di tabelle che rappresentano ciascuna un mese di calendario / anno. È quindi possibile creare viste per collegarle insieme ai report. Logicamente, questa è tutta una tabella molto grande, ma troncarla semplifica i lavori di manutenzione del DB. Tuttavia, presenta il problema di come gestire gli inserti in più tabelle (consentendo di iniziare a registrare le transazioni nel mese successivo pur chiudendo l'ultimo) senza finire con chiavi duplicate. Ancora una volta, i GUID invece delle colonne di numeri interi di identità sono la soluzione ideale, poiché il DBMS è progettato per generarli in un modo davvero unico,

Esistono soluzioni alternative che consentono l'uso delle colonne di identità in queste situazioni, come ho sperato di aver menzionato, ma nella maggior parte di questi, l'aggiornamento dalla colonna di numeri interi di identità a un GUID è più semplice e risolve il problema in modo più completo.


1
In alcuni casi è ancora possibile richiedere l'ID nelle tabelle M: ​​N (utilizzando le colonne ID, ID_M, ID_N) a causa del collegamento di proprietà alle istanze della relazione M: N.
miroxlav,

I GUID V4 non sono garantiti per usare un PNRG crittograficamente forte, quindi non dovresti davvero fare affidamento su di esso per il tuo primo esempio di imo (anche se se il tuo motore db promette più forte potresti andare bene, ma è piuttosto non portatile). Altrimenti un post ben ragionato.
Voo,

1
@miroxlav - Direi che se una tabella ha abbastanza metadati aggiuntivi riguardo alla relazione che un PK separato al di fuori dei due FK è una buona idea, non è più una tabella di riferimenti incrociati; è la sua stessa entità che fa riferimento alle altre due.
KeithS

@Voo - Hai ragione, i GUID V4 non sono garantiti per essere crittograficamente casuali, solo unici (come tutti i GUID lo sono). Tuttavia, i numeri di coda dei caccia aerei statunitensi non sono nemmeno generati da dati / algoritmi di seme crittograficamente casuali. Quello che stai veramente cercando è un dominio scarsamente popolato; un GUID V4 ha 112 byte di dati casuali, in grado di identificare in modo univoco record 5e33.
KeithS

Per mettere in prospettiva quel numero, ogni uomo, donna e bambino sul pianeta (tutti i 7 miliardi) potrebbe avere 741 trilioni di punti dati catalogati e identificati individualmente nel nostro DB, e continueremmo ad usare solo un valore GUID per miliardo disponibile. I Big Data, in quanto settore globale, non sono nemmeno vicini a questa scala di conoscenza. Anche dato uno schema alla generazione GUID, ci sono altre fonti di entropia coinvolte, come l'ordine in cui i dati entrano nel sistema e gli viene assegnato un GUID.
KeithS

7

Una chiave primaria auto-incrementata (identità) è una buona idea se non per notare che è priva di significato al di fuori del contesto del database e dei client immediati di quel database. Ad esempio, se trasferisci e memorizzi alcuni dei dati in un altro database, quindi procedi a scrivere dati diversi su entrambe le tabelle del database, gli ID divergeranno, ovvero i dati con un ID di 42 in un database non corrisponderanno necessariamente ai dati con un ID di 42 nell'altro.

Detto questo, se è necessario essere ancora in grado di identificare le righe in modo univoco al di fuori del database (e spesso lo è), è necessario disporre di una chiave diversa per questo scopo. Una chiave aziendale attentamente selezionata farà, ma spesso finirai nella posizione di un gran numero di colonne necessarie per garantire l'univocità. Un'altra tecnica consiste nell'avere una colonna Id come chiave primaria cluster con incremento automatico e un'altra colonna uniqueidentifier (guid) come chiave univoca non cluster, allo scopo di identificare in modo univoco la riga in qualunque parte del mondo esista. La ragione per cui hai ancora una chiave auto-incrementata in questo caso è perché è più efficiente raggruppare e indicizzare la chiave auto-incrementante piuttosto che fare la stessa cosa con un guid.

Un caso in cui potresti non desiderare una chiave a incremento automatico sarebbe una tabella molti-a-molti in cui la chiave primaria è un composto delle colonne Id di altre due tabelle (potresti comunque avere una chiave a incremento automatico qui, ma io non ne vedo il punto).

Un'altra domanda è il tipo di dati della chiave auto-incrementata. L'uso di Int32 offre un intervallo di valori ampio ma relativamente limitato. Personalmente uso spesso colonne bigint per l'ID, per non aver praticamente mai bisogno di preoccuparmi di rimanere senza valori.


6

Come altre persone hanno avanzato la richiesta di una chiave primaria incrementale, ne farò una per un GUID:

  • È garantito per essere unico
  • Puoi avere un viaggio in meno nel database per i dati nella tua applicazione. (Ad esempio, per una tabella dei tipi è possibile memorizzare il GUID nell'applicazione e utilizzarlo per recuperare il record. Se si utilizza un'identità è necessario interrogare il database per nome e ho visto molte applicazioni che lo fanno per ottenere il PK e successivamente lo interroga di nuovo per ottenere tutti i dettagli).
  • È utile per nascondere i dati. www.domain.com/Article/2 Fammi sapere che hai solo due articoli mentre www.domain.com/article/b08a91c5-67fc-449f-8a50-ffdf2403444a non mi dice nulla.
  • È possibile unire facilmente record da diversi database.
  • MSFT utilizza GUIDS per l'identità.

Modifica: punto duplicato


5
-1. Un GUID / UUID non è garantito per essere unico e non è unico al 100%. Un GUID ha ancora una lunghezza limitata, quindi ad un certo punto puoi rischiare di ottenere un duplicato, anche se è altamente improbabile. Anche il punto su meno viaggi nel database non è valido: perché non è possibile memorizzare l'id primario nell'applicazione, come è possibile con la chiave GUID?
Niklas H,

2
Jeff Atwood lo dice molto meglio di quanto io abbia mai potuto. blog.codinghorror.com/primary-keys-ids-versus-guids
Three Value Logic

Per quanto riguarda il motivo per cui non è possibile memorizzare l'id principale nella propria applicazione? Perché il database lo crea. Se esegui i tuoi seed su un database vuoto, puoi presumere che l'ID sarà 1. Cosa succede se esegui lo stesso script su un database con dati al suo interno? L'ID non sarà 1.
Three Value Logic

Non hai detto nulla sulla creazione di ID nell'applicazione - hai appena scritto "memorizzazione". Ma se è necessario creare l'ID al di fuori del database, sì, un GUID potrebbe essere la risposta.
Niklas H,

2
Vorrei aggiungere che ridimensionano meglio. I database NoSQL per big data come Cassandra non supportano nemmeno le chiavi di incremento automatico.
Karl Bielefeldt,

2

Come principio di buon design, ogni tavolo dovrebbe avere un modo affidabile per identificare in modo univoco una riga. Anche se è a questo che serve una chiave primaria, non sempre richiede l'esistenza di una chiave primaria. L'aggiunta di una chiave primaria a ogni tabella non è una cattiva pratica poiché fornisce l'identificazione univoca della riga, ma potrebbe non essere necessaria.

Per mantenere relazioni affidabili tra le righe di due o più tabelle, è necessario farlo tramite chiavi esterne, quindi la necessità di chiavi primarie in almeno alcune tabelle. L'aggiunta di una chiave primaria a ogni tabella semplifica l'estensione della progettazione del database quando arriva il momento di aggiungere nuove tabelle o relazioni ai dati esistenti. Pianificare in anticipo è sempre una buona cosa.

Come principio di base (forse regola rigida), il valore di una chiave primaria non dovrebbe mai cambiare per tutta la durata della riga. È saggio supporre che tutti i dati aziendali in una riga siano soggetti a modifiche nel corso della sua vita, quindi qualsiasi dato aziendale sarà un candidato scadente per una chiave primaria. Ecco perché qualcosa di astratto come un numero intero auto-incrementato è spesso una buona idea. Tuttavia, i numeri interi auto-incrementati hanno i loro limiti.

Se i tuoi dati avranno una vita solo all'interno del tuo database, gli interi con incremento automatico vanno bene. Ma, come è stato menzionato in altre risposte, se vuoi che i tuoi dati vengano condivisi, sincronizzati o che abbiano una vita al di fuori del tuo database, numeri interi auto-incrementati diventano chiavi primarie scadenti. Una scelta migliore sarà un guid (alias uuid "ID universalmente unico").


2

La domanda e molte delle risposte mancano del punto importante che tutte le chiavi naturali per ogni tabella risiedono esclusivamente nello schema logico per il database e tutte le chiavi surrogate per ogni tabella risiedono esclusivamente nello schema fisico per il database. altre risposte discutono solo i vantaggi relativi delle chiavi surrogate integer rispetto a GUID, senza discutere i motivi per cui le chiavi surrogate sono usate correttamente e quando.

BTW: Evitiamo l'uso del termine chiave mal definito e impreciso . Si tratta di un artefatto di modelli di dati pre-relazionali che è stato inizialmente cooptato (involontariamente) nel modello relazionale e poi cooptato di nuovo nel dominio fisico da vari fornitori di RDBMS. Il suo uso serve solo a confondere la semantica.

Si noti dal modello relazionale che, affinché lo schema logico del database sia nella prima forma normale , ogni tabella deve avere un set di campi visibile dall'utente, noto come chiave naturale, che identifica in modo univoco ogni riga della tabella. Nella maggior parte dei casi, tale chiave naturale viene prontamente identificata, ma a volte è necessario costruirla, sia come campo di pareggio che in altro modo. Tuttavia, tale chiave costruita è sempre visibile all'utente e pertanto risiede sempre nello schema logico del database.

Al contrario, qualsiasi chiave surrogata su una tabella risiede puramente nello schema fisico del database (e quindi deve sempre essere, sia per motivi di sicurezza che per il mantenimento dell'integrità del database, completamente invisibile agli utenti del database). L'unico motivo per introdurre una chiave surrogata è quello di affrontare i problemi di prestazioni nella manutenzione fisica e nell'uso del DB; che si tratti di join, replica, più origini hardware per dati o altro.

Poiché l'unica ragione per l'introduzione di una chiave surrogata è la prestazione, supponiamo che desideriamo che sia performante. Se il problema di prestazioni a portata di mano è unirsi, allora desideriamo necessariamente rendere la nostra chiave surrogata più stretta possibile (senza intralciare l'hardware, quindi numeri interi e byte brevi di solito sono fuori). Le prestazioni di join si basano su un'altezza minima dell'indice, quindi un numero intero a 4 byte è una soluzione naturale. Se il tuo problema di prestazioni è la frequenza di inserzione, un numero intero a 4 byte può anche essere una soluzione naturale (a seconda degli interni del tuo RDBMS). Se il problema di prestazioni di una tabella è la replica o più origini dati rispetto ad altre tecnologie surrogate , può essere più adatto un GUID o una chiave in due parti (ID host + intero). Personalmente non sono un favorito dei GUID ma sono convenienti.

Per riassumere, non tutte le tabelle richiedono una chiave surrogata (di qualsiasi tipo); dovrebbero essere utilizzati solo quando ritenuto necessario per l'esecuzione della tabella in esame. Indipendentemente dalla tecnologia chiave surrogata comune che preferisci, pensa attentamente alle effettive esigenze del tavolo prima di fare una scelta; cambiare la scelta della tecnologia chiave surrogata per un tavolo sarà un lavoro estenuante. Documenta la metrica delle prestazioni chiave per la tua tabella in modo che i tuoi successori capiscano le scelte fatte.

Casi speciali

  1. Se i requisiti aziendali impongono una numerazione sequenziale delle transazioni a fini di audit (o altro) diversa da tale campo non è una chiave sostitutiva; è una chiave naturale (con requisiti extra). Dalla documentazione un numero intero auto-incrementante genera solo chiavi surrogate , quindi trova un altro meccanismo per generarlo. Ovviamente sarà necessaria una sorta di monitor, e se stai acquistando le tue transazioni da più siti, un sito sarà speciale , in quanto è il sito host designato per il monitor.

  2. Se la tabella non sarà mai più di circa un centinaio di righe, l'altezza dell'indice è irrilevante; ogni accesso avverrà tramite una scansione della tabella. Tuttavia i confronti di stringhe su stringhe lunghe saranno ancora molto più costosi del confronto di un numero intero a 4 byte e più costosi del confronto di un GUID.

  3. Una tabella di valori di codice digitati da un campo di codice char (4) dovrebbe essere performante come uno con un numero intero di 4 byte. Anche se non ne ho la prova, uso spesso il presupposto e non ho mai avuto motivo di affrontarlo.


-1

Non solo non è una buona pratica, in realtà è descritto come un anti-pattern nel libro SQL Antipatterns di Bill Karwin.

Non tutte le tabelle hanno bisogno di uno pseudokey - una chiave primaria con un valore arbitrario, non qualcosa che abbia un valore semantico per il modello - e non c'è motivo di chiamarlo sempre id.


questo non sembra offrire nulla di sostanziale rispetto ai punti formulati e spiegati nelle precedenti risposte 9
moscerino del

2
e perché questo potrebbe essere importante?
moscerino del

3
@gnat Perché è un libro sulle migliori pratiche, che affronta direttamente la domanda. Non è ovvio?
Pedro Werneck,

3
non il minimo. La ricerca di Google per "best practice sql del libro" mostra circa 900K di link per me, perché questo dovrebbe essere particolarmente degno
moscerino

1
@gnat Non ho intenzione di discutere tutto il giorno. Non ti piace la risposta, ecco a cosa servono i voti negativi.
Pedro Werneck,

-2

Questo è piuttosto universale, altrimenti dovresti convalidare che la chiave è effettivamente unica. Questo sarebbe fatto guardando tutte le altre chiavi ... il che richiederebbe molto tempo. Avere una chiave incrementale diventa costoso quando il numero del record si avvicina al valore di overflow della chiave.

Di solito, i puntatori rendono i nomi dei campi più ovvi ref_{table}o un'idea simile.

Se non è necessario indicare esternamente un record, non è necessario un ID.


Valore di rollover chiave?
AJJ

Un numero intero senza segno ha un valore massimo di 4294967295 prima di aggiungere 1 lo farà scorrere su 0. Ricorda che se aggiungi un record quindi lo elimini, il contatore viene comunque aumentato. Assicurati di utilizzare unsigned intper il tipo di campo, altrimenti il ​​limite è la metà di quel numero.
Johnny V,


2
Se aggiungi / rimuovi molte righe, il contatore di incremento automatico alla fine trabocca.
Johnny V,

1
In che modo le persone gestiscono il rollover? Che cosa succede se ci sono record con un ID basso che non vengono mai eliminati, ma stai iniziando a raggiungere la fine in cui alcuni ID si trovano nella parte superiore di 4294967295? È possibile effettuare una "reindicizzazione"?
AJJ,

-2

Non direi che dovrebbe sempre essere fatto. Ho un tavolo qui senza chiave univoca e non ne ha bisogno. È un registro di controllo. Non ci sarà mai un aggiornamento, le query restituiranno tutte le modifiche a ciò che viene registrato, ma questo è il migliore che si possa ragionevolmente fare, ci vuole un essere umano per definire un cambiamento errato. (Se il codice avesse potuto, in primo luogo non lo avrebbe consentito!)


-3

Un contatore di incremento automatico per una chiave primaria non è una buona idea. Questo perché è necessario tornare al database per trovare la chiave successiva e incrementarla di una prima di inserire i dati.

Detto questo, in genere utilizzerei tutto ciò che il database può fornire per la chiave primaria anziché averla come parte dell'applicazione.

Lasciando che il database lo fornisca nativamente per te, può garantire che la chiave sia unica per ciò di cui ha bisogno.

Naturalmente non tutti i database lo supportano. Nel qual caso utilizzo generalmente una tabella in cui sono archiviati i bucket delle chiavi e utilizzo intervalli alti e bassi gestiti nell'applicazione. Questa è la soluzione più efficace che trovo perché ottieni un intervallo di 10000 numeri e li incrementa automaticamente sull'istanza dell'applicazione. Un'altra istanza dell'applicazione può raccogliere un altro bucket di numeri con cui lavorare. È necessaria una primitiva di chiave primaria sufficientemente grande come una lunghezza di 64 bit.

UUID che non utilizzo come chiavi primarie perché il costo di costruirle e memorizzarle è molto più elevato che incrementare un valore lungo di una. Gli UUID affrontano ancora il paradosso del compleanno in quanto teoricamente può sorgere un duplicato.


3
No. chiavi autoincremento significa che l'incremento della chiave viene eseguito automaticamente dal database. A volte (ti sto guardando, Oracle!) Hai bisogno di una combinazione sequenza + trigger per farlo, ma non hai mai bisogno di cercare il valore precedentemente inserito per la chiave, aggiungere 1, quindi usarlo.
SQB,

Con alcuni framework di persistenza come JPA se si desidera restituire al chiamante il valore della chiave che è stata creata, è necessario caricare il record per vedere la chiave.
Archimede Trajano,
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.