SQL: chiave primaria della tabella molti-a-molti


125

Questa domanda viene fuori dopo aver letto un commento in questa domanda:

Progettazione di database

Quando crei una tabella molti-a-molti, dovresti creare una chiave primaria composita sulle due colonne della chiave esterna, o creare una chiave primaria "ID" surrogata con incremento automatico e inserire semplicemente gli indici nelle due colonne FK (e forse un vincolo unico)? Quali sono le implicazioni sulle prestazioni per l'inserimento di nuovi record / la reindicizzazione in ogni caso?

Fondamentalmente, questo:

PartDevice
----------
PartID (PK/FK)
DeviceID (PK/FK)

contro questo:

PartDevice
----------
ID (PK/auto-increment)
PartID (FK)
DeviceID (FK)

Il commentatore dice:

rendere i due ID PK significa che la tabella è fisicamente ordinata sul disco in quell'ordine. Quindi, se inseriamo (Part1 / Device1), (Part1 / Device2), (Part2 / Device3), allora (Part 1 / Device3) il database dovrà separare la tabella e inserire l'ultima tra le voci 2 e 3. Per molti record, questo diventa molto problematico in quanto comporta lo spostamento di centinaia, migliaia o milioni di record ogni volta che ne viene aggiunto uno. Al contrario, un PK autoincrementante consente di attaccare i nuovi record alla fine.

Il motivo per cui lo chiedo è perché sono sempre stato propenso a fare la chiave primaria composita senza colonna di incremento automatico surrogato, ma non sono sicuro che la chiave surrogata sia effettivamente più performante.


Ecco una domanda su Silimar pubblicata su SO: stackoverflow.com/questions/344068/…
Tony

(Ho provato ad aggiungerlo al mio commento precedente, ma non posso) A seconda del numero di inserimenti, puoi anche ricostruire periodicamente il tuo indice per assicurarti che restituisca risultati rapidamente. In SQL Server è anche possibile modificare il FILLFACTOR dell'indice per fornire spazio sufficiente per gli inserimenti prima che sia necessario spostare i dati.
Tony

1
La risposta a questo non dipende da quale DBMS viene utilizzato? Sospetto che MySQL si comporterà in un modo in questo caso, SQL-Server leggermente in un altro modo, ecc.
Radu Murzea,

Avvertenza: senza un tag di database specifico, molto di ciò che viene detto qui è sospetto. Motori diversi funzionano in modo diverso!
Rick James

Risposte:


85

Con una semplice mappatura molti-a-molti a due colonne, non vedo alcun vantaggio reale nell'avere una chiave surrogata. Avere una chiave primaria su (col1,col2)è garantito unico (supponendo che i tuoi valori col1e col2nelle tabelle di riferimento siano univoci) e un indice separato su (col2,col1)catturerà quei casi in cui l'ordine opposto verrebbe eseguito più velocemente. Il surrogato è uno spreco di spazio.

Non avrai bisogno di indici sulle singole colonne poiché la tabella dovrebbe essere utilizzata solo per unire insieme le due tabelle di riferimento.

Quel commento a cui fai riferimento nella domanda non vale gli elettroni che usa, secondo me. Sembra che l'autore pensi che la tabella sia memorizzata in un array piuttosto che in una struttura ad albero a più vie bilanciata ad altissime prestazioni.

Per cominciare, non è mai necessario memorizzare o ordinare al tavolo , solo l'indice. E l'indice non verrà archiviato in sequenza, verrà archiviato in modo efficiente per poter essere recuperato rapidamente.

Inoltre, la stragrande maggioranza delle tabelle del database viene letta molto più spesso che scritta. Ciò rende tutto ciò che fai sul lato selezionato molto più rilevante di qualsiasi cosa sul lato dell'inserto.


L'ultimo punto non è una buona generalizzazione: "la stragrande maggioranza delle tabelle del database viene letta molto più spesso che scritta". Trovo molti esempi di tabelle associative su cui è necessario scrivere molto spesso, ad esempio una tabella che collega il cliente all'ordine.
utente

5
@buffer, rispetterò quel commento (tecnicamente, è una generalizzazione solo se dico "tutti i tavoli", "stragrande maggioranza" si basa sull'esperienza). Pensiamo anche al tuo esempio, un ordine viene creato una volta (potrebbe essere aggiornato occasionalmente ma è improbabile che cambi le informazioni chiave / indice, più per colpire cose come lo stato dell'ordine. Tuttavia, quegli aggiornamenti e le selezioni che dovrai fare per stampare fatture o generare rapporti di gestione supereranno l'inserto originale.
paxdiablo

Pensa ad Amazon: migliaia di ordini creati ogni ora.
utente

9
@ buffer, sì, ma ancora una volta, ciascuno di quegli ordini verrà quasi certamente interrogato molte volte per eseguire (ad esempio) pacchetti, fatturazione, aggiornamenti di stato, analisi aziendali e così via. Il numero assoluto di creazioni è meno importante del rapporto tra creazioni e letture.
paxdiablo

1
Il punto è, insertimporterà se viene fatto migliaia di volte all'ora. Non puoi semplicemente ignorarlo solo perché il rapporto tra inserta selectè <1. In questo caso, un cliente si preoccupa di quanto tempo impiega per effettuare un ordine.
utente

19

Non è necessaria alcuna chiave surrogata per le tabelle di collegamento.

Un PK su (col1, col2) e un altro indice univoco su (col2, col1) è tutto ciò di cui hai bisogno

A meno che tu non utilizzi un ORM che non è in grado di far fronte e impone il tuo progetto DB per te ...

Modifica: ho risposto lo stesso qui: SQL: hai bisogno di una chiave primaria auto-incrementale per le tabelle Many-Many?


3
Potresti essere OK con un indice dups su col2 invece di un indice univoco su (col2, col1). Il vantaggio dell'indice a due colonne è che consente scansioni solo indice su col2 da solo o su col1 e col2 (sebbene l'altro indice, su (col1, col2) gestisce anche il caso "entrambi"). Lo svantaggio è lo spazio di archiviazione aggiuntivo necessario per la colonna aggiuntiva. Questo di solito non è significativo, quindi il consiglio è tutt'altro che orribile. Tuttavia, se col1 e col2 sono grandi o di dimensioni molto diverse, puoi risparmiare un po 'di spazio senza danneggiare le prestazioni scegliendo di avere il secondo indice solo sulla colonna più corta.
Jonathan Leffler

@gbn: il secondo indice su (col2, col1) non deve essere univoco, giusto?
utente

1
mettere un indice univoco su (col1, col2) dopo che è già un PK è completamente ridondante
Don Cheadle

@mmcrae: dove lo stiamo facendo?
gbn

2
@mmcrae: il tuo commento è "mettere un indice univoco su (col1, col2) ..". L'ordine delle colonne in un indice è importante. (col2, col1)non lo è (col1, col2). Il PK di (col1, col2)potrebbe non essere adatto a tutte le query e generare scansioni, quindi avere il contrario migliora le prestazioni perché consente di cercare dove col2 è migliore. Ad esempio, la convalida FK quando la tabella con col2 ha un'eliminazione. Il tavolo figlio deve essere controllato
gbn

12

Se si fa riferimento alla tabella, potrebbe essere necessaria una chiave primaria incrementale. Potrebbero esserci dettagli nella tabella molti-a-molti che dovevano essere estratti da un'altra tabella utilizzando la chiave primaria incrementale.

per esempio

PartDevice
----------
ID (PK/auto-increment)
PartID (FK)
DeviceID (FK)
Other Details

È facile estrarre gli "Altri dettagli" utilizzando PartDevice.ID come FK. Pertanto è necessario l'uso della chiave primaria incrementale.


1
Grazie! Sono arrivato alla risposta perché stavo cercando quasi lo stesso scenario che hai descritto. Ma ti sei allontanato dalla tua prima frase aggiungendo "Altri dettagli". E se avessi una tabella di mappatura molti a molti, a cui devo fare riferimento da un'altra tabella? Significa che la tabella di mappatura molti a molti non ha memorizzato altre informazioni ... La colonna ID aggiuntiva avrebbe comunque senso? In caso contrario, come fare invece riferimento a un record della tabella di mappatura?
misantropo

Ci sono due opzioni qui, puoi usare la chiave composta come chiave esterna dalla tua tabella di riferimento (questo aggiunge una colonna extra alla tua nuova tabella), oppure puoi creare una colonna id alla tabella di mappatura e impostare un vincolo univoco al composto originale chiave primaria mentre la nuova colonna id diventerà la chiave primaria.
Vočko

6

Il modo più breve e diretto con cui posso rispondere alla tua domanda è dire che ci sarà un impatto sulle prestazioni se le due tabelle che stai collegando non hanno chiavi primarie sequenziali. Come hai affermato / citato, l'indice per la tabella dei collegamenti diventerà frammentato o il DBMS lavorerà di più per inserire i record se la tabella dei collegamenti non ha la propria chiave primaria sequenziale. Questo è il motivo per cui la maggior parte delle persone inserisce una chiave primaria con incremento sequenziale nelle tabelle di collegamento.


2

Quindi sembra che se l'UNICO compito fosse collegare le due tabelle, il miglior PK sarebbe il PK a doppia colonna.

Ma se serve ad altri scopi, aggiungi un altro NDX come PK con una chiave esterna e un secondo indice univoco.

Index o PK è il modo migliore per assicurarsi che non ci siano duplicati. PK consente a strumenti come Microsoft Management Studio di svolgere parte del lavoro (creazione di visualizzazioni) per te

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.