2009-05-19 11 views
17

Stavo guardando uno screencast in cui l'autore ha detto che non è bello avere una chiave primaria su un tavolo join ma non ha spiegato il perché.Perché non è opportuno avere una chiave primaria su una tabella di join?

La tabella di join nell'esempio aveva due colonne definite in una migrazione di Rails e l'autore ha aggiunto un indice a ciascuna delle colonne ma nessuna chiave primaria.

Perché non è utile avere una chiave primaria in questo esempio?

create_table :categories_posts, :id => false do |t| 
    t.column :category_id, :integer, :null => false 
    t.column :post_id, :integer, :null => false 
end 
add_index :categories_posts, :category_id 
add_index :categories_posts, :post_id 

EDIT: Come ho già detto a Cletus, posso capire la potenziale utilità di un campo numero di auto come chiave primaria anche per un tavolo aderire. Tuttavia nell'esempio che ho elencato sopra, l'autore evita esplicitamente di creare un campo numerico automatico con la sintassi ": id => false" nell'istruzione "create table". Normalmente Rails aggiungerebbe automaticamente un campo di identificazione automatica a una tabella creata in una migrazione come questa e questa diventerebbe la chiave primaria. Ma per questa tabella di join, l'autore lo ha specificamente prevenuto. Non ero sicuro del motivo per cui ha deciso di seguire questo approccio.

+0

Ai redattori: potrebbe essere importante sottolineare il contesto di questa domanda. È più spesso che non una cattiva forma per NON avere una chiave primaria. –

+0

Il buon articolo è il saggio del Codd del 1970 http://www.seas.upenn.edu/~zives/03f/cis550/codd.pdf –

+0

Una cosa da considerare, quella carta fu scritta nel 1970, quando I/O e l'archiviazione dei dati era relativamente, molto più costoso. Nei tempi moderni, tuttavia, i costi di aggiunta di una colonna chiave primaria aggiuntiva sono quasi sempre minuscoli. Mi piacerebbe vedere qualcuno presentare un caso reale in cui la colonna in più crea un problema misurabile. – DGM

risposta

38

Alcune note:

  1. La combinazione di category_id e post_id è unico in se stesso, in modo da una colonna ID aggiuntiva è ridondante e dispendioso
  2. La frase "non bene avere una chiave primaria" non è corretto nello screencast. Hai ancora una chiave primaria - è composta solo dalle due colonne (ad esempio CREATE TABLE pippo (cid, pid, PRIMARY KEY (cid, pid)). Per le persone che sono abituate a virare sui valori ID ovunque questo può sembrare dispari ma in teoria relazionale è del tutto corretto e naturale, l'autore dello screencast avrebbe detto che "non è bello avere un attributo intero implicito chiamato 'ID' come chiave primaria."
  3. È ridondante avere l'extra colonna perché si posizionerà un indice univoco sulla combinazione di id_categoria e post_id in ogni caso per garantire che non vengano inserite righe duplicate
  4. Infine, anche se la nomenclatura comune è chiamarla "chiave composita", anche questa è ridondante. in teoria relazionale è in realtà l'insieme di zero o più attributi che identificano in modo univoco la riga, quindi è corretto dire che la chiave primaria è categorizzata y_id, post_id
  5. Inserire la colonna MIGLIORE SELETTIVO nella dichiarazione della chiave primaria. Una discussione sulla costruzione di alberi b (+/*) è fuori dallo scopo di questa risposta (per alcune discussioni di livello inferiore vedi: http://www.akadia.com/services/ora_index_selectivity.html) ma nel tuo caso probabilmente lo vorrai su post_id, category_id dato che post_id mostra meno spesso nella tabella e quindi rende l'indice più utile. Ovviamente, poiché la tabella è così piccola e l'indice sarà, essenzialmente, le righe di dati, questo non è molto importante. Sarebbe nei casi più ampi in cui il tavolo è più ampio.
+0

Questa spiegazione ha colpito tutti i punti di non chiarezza su cui mi stavo bloccando. Grazie. "l'autore dello screencast avrebbe detto che non era bello avere un attributo intero implicito chiamato 'ID' come chiave primaria '". E grazie per averlo spiegato: "È ridondante avere la colonna in più perché si posizionerà un indice univoco sulla combinazione di category_id e post_id in ogni caso per garantire che non siano inserite righe duplicate " –

+0

Non è corretto dire zero o più (teoria degli insiemi di base a parte) vedi: http://www.seas.upenn.edu/~zives /03f/cis550/codd.pdf –

+0

OK, va bene nella teoria relazionale (set vuoto come chiave) ma NON in SQL –

3

Un DBA potrebbe dirvi che la chiave primaria in questo caso è in realtà la combinazione delle due colonne FK. Dal momento che Rails/ActiveRecord non gioca bene con i PK compositi (di default, almeno), questa potrebbe essere la ragione.

+0

Quando dici "di default" intendi che c'è un modo per far sì che Rails giochi bene, ma è complicato da fare? –

+0

q-tip: dai un'occhiata a has_and_belongs_to_many (insieme a has_many_through) http://blog.hasmanythrough.com/2007/1/15/basic-rails-association-cardinality - si tratterà magicamente (in qualche modo) del join table –

+0

@po Ho * sentito * che ci sono plugin per far funzionare AR con chiavi naturali (incluse le chiavi composite) invece di chiavi surrogate. Non so se funzionino o meno. –

3

La combinazione di chiavi esterne può essere una chiave primaria (chiamata chiave primaria composita). Personalmente preferisco usare una chiave primaria tecnica invece di quella (campo numero automatico, sequenza, ecc.). Perché? Bene, lo rende molto più facile da identificare il record, che potrebbe essere necessario fare se hai intenzione di eliminarlo.

Pensateci: se avete intenzione di presentare una pagina Web di tutti i collegamenti, avere una chiave primaria per identificare il record rende molto più facile.

+0

Vedo cosa intendi per l'utilità di un campo del numero automatico come chiave primaria anche per una tabella di join. Tuttavia nell'esempio che ho elencato sopra, l'autore evita esplicitamente di creare un campo numerico automatico con la sintassi ": id => false" nell'istruzione create table. Normalmente Rails aggiungerebbe automaticamente un campo di identificazione automatica a una tabella creata in una migrazione come questa e questa diventerebbe la chiave primaria. Ma per questa tabella di join, l'autore lo ha specificamente prevenuto. Non ero sicuro del motivo per cui ha deciso di seguire questo approccio. –

3

Fondamentalmente perché non ce n'è bisogno. La combinazione del campo con due chiavi esterne identifica in modo univoco qualsiasi riga.

Ma questo dice semplicemente perché non è una buona idea .... ma perché sarebbe una cattiva idea?

Considerare il sovraccarico aggiungendo una colonna di identità. La tabella occuperebbe il 50% in più di spazio su disco. Peggio è la situazione dell'indice. Con un campo identità, devi mantenere il conteggio delle identità, oltre a un secondo indice. Potrai triplicare lo spazio su disco e triplicare il lavoro da eseguire su ogni inserto. Con l'unico vantaggio è una clausola WHERE leggermente più breve in un comando DELETE.

D'altra parte, se i campi chiave compositi sono l'intera tabella, l'indice può essere la tabella.

+1

Tutte le chiavi primarie non sono colonne identitarie! –

+0

Siamo spiacenti. Ti stavo seguendo fino all'ultima frase. Non ho capito cosa intendevi per: "D'altra parte, se i campi chiave compositi sono l'intera tabella, l'indice può essere la tabella." –

+0

q-tip: Non c'è alcun vantaggio nell'aggiungere un indice uguale all'intera tabella, come accadrebbe se la tua chiave primaria fosse una chiave composta composta da ogni colonna della tabella. Quando facciamo ciò, la tabella funge da indice. Spero sia utile. Scusa se non lo è. –

3

È una cattiva idea non avere una chiave primaria su alcuna tabella, punto (se il DBMS è un DBMS relazionale o un DBMS SQL). Le chiavi primarie sono una parte cruciale dell'integrità del tuo database.

Suppongo che se non ti dispiace che il tuo database sia inaccurato e fornisca risposte sbagliate ogni tanto, allora ne puoi fare a meno ... ma la maggior parte delle persone vuole risposte precise dal loro DBMS e per queste persone, le chiavi primarie sono cruciali .

+0

Che tipo di dati inaccurati impedirebbe una chiave primaria in questa tabella di join costituita da due colonne? Non dubito che tu abbia ragione ma non capisco che tipo di dati non validi potrebbero essere possibili qui. Puoi farmi un esempio? –

+0

Righe duplicate, immagino. –

+0

Le righe duplicate sono ciò che avevo in mente. Puoi trovare anche altri esempi, specialmente con colonne di identità disseminate liberamente e nessun vincolo univoco sulle chiavi naturali. Se la tabella ha altre colonne oltre alle colonne chiave, puoi effettivamente avere il database che contiene le contraddizioni - e se conosci la logica classica, sai che argomentare da una contraddizione porta a errori. –

1

favore di avere un unico PK

  • identifica in modo univoco una riga con un unico valore
  • è facilissimo riferimento alla rapporto da altrove se necessario
  • Alcuni strumenti vogliono per avere un unico valore intero pk

Contro di avere un unico PK

  • utilizza più spazio su disco
  • bisogno di 3 indici piuttosto che 1
  • Senza un vincolo univoco che si potrebbe finire con più righe per lo stesso rapporto

Note

  • È necessario definire un vincolo univoco se si desidera evitare i duplicati
  • A mio parere non utilizzare il singolo pk se la tabella è destinata a essere enorme, altrimenti scambiare parte dello spazio su disco per comodità. Sì, è uno spreco, ma a chi importa di pochi MB su disco nelle applicazioni del mondo reale.
+0

"Sono necessari 3 indici invece di 1" - Presumo intendi la chiave primaria su un campo del numero automatico più gli altri due indici nel mio esempio sopra (non due indici aggiuntivi non elencati nel mio esempio). "Senza un vincolo univoco potresti finire con più righe per la stessa relazione" -> Quindi in quel caso il PK dovrebbe fare riferimento alle due colonne in questa tabella di join. In altre parole, una chiave primaria che consiste semplicemente in un campo del numero automatico non funzionerebbe. Spero di capirmi. –

2

Il posizionamento della colonna più selettiva deve essere solo rilevante nella dichiarazione INDICE.Nella dichiarazione KEY, non dovrebbe avere importanza (perché, come è stato correttamente sottolineato, il KEY è un SET, e all'interno di un set, l'ordine non ha importanza - l'insieme {a1, a2} è lo stesso di {a2 , a1}).

Se un prodotto DBMS è tale che l'ordine degli attributi all'interno di una dichiarazione KEY fa la differenza, allora quel prodotto DBMS è colpevole di non distinguere correttamente tra la progettazione logica di un database (la parte in cui si fa la dichiarazione KEY) e la struttura fisica del database (la parte in cui si fa la dichiarazione INDICE).

+0

La maggior parte, se non tutti, i prodotti DBMS che ho utilizzato (MySQL, Sybase ASE, SQL Server, Oracle) creano implicitamente un indice univoco sulla dichiarazione PRIMARY KEY nell'ordine specificato. Sì, sta violando l'indipendenza logica/fisica, ma è l'unica strada da percorrere (a meno che non crei la tabella con una chiave primaria, crei un indice univoco, quindi "contrassegni" la chiave primaria in qualche modo) –

+0

Inoltre, SQL per definizione viola un TON del modello relazionale, questo incluso: D –

2

Desidero commentare il seguente commento: "Non è corretto dire zero o più".

Volevo sottolineare che il testo a cui questo commento è stato aggiunto semplicemente non conteneva il testo "zero o più", quindi l'autore del commento che volevo commentare stava criticando qualcun altro per qualcosa che non aveva stato detto

Volevo anche commentare che non è corretto dire che non è corretto dire "zero o più". La teoria relazionale comunemente conosciuta oggi tra le poche persone che ancora si preoccupano di studiare i dettagli di quella teoria, in realtà RICHIEDE la possibilità di una chiave senza attributi.

Ma quando ho premuto il pulsante "commento", il sistema ha risposto a me che il commento richiede un punteggio di reputazione di 50 (o alcuni di questi).

Una triste illustrazione di come il mondo sembra aver dimenticato che la scienza non è la democrazia, e che nella scienza, la verità non è determinata da chi capita di essere la maggioranza, né da chi capita di avere "sufficiente reputazione".

+0

Vedo - la rilettura del database del dattero della data dice che un PK vuoto viene usato per vincolare i relvars su una singola riga. OK, lo compro - non è esplicito nello scritto di Codd ma a parte questo caso limitato, quando qualcuno userebbe una chiave vuota? –

Problemi correlati