Devo aggiungere un campo di incremento automatico / IDENTITÀ a una tabella di riferimenti incrociati solo per scopi PK?


9

Sto aggiungendo la seguente tabella di riferimenti incrociati al mio DB ospitato da SQL Server:

company_id bigint not null (FK)
org_path nvarchar (2048) not null

Il company_idcampo si riferisce al idcampo in un'altra tabella (in cui è la chiave primaria).

Dato che possono esserci anche più record con lo stesso company_id, qualsiasi chiave primaria dovrebbe usare entrambi i campi. Tuttavia, non riesco a creare una chiave utilizzando entrambi i campi perché org_pathè troppo lungo per SQL Server.

Per quanto riguarda org_path, questa è l'unica tabella in cui esiste. È probabile che le query a questa tabella richiedano tutte le voci o tutte le org_pathvoci company_id. O, per dirla in altro modo, sembra dubbio che questo tavolo verrà mai interrogato org_path. Inoltre, è improbabile che org_pathvenga aggiornato, più probabilmente inserito e - probabilmente raramente - eliminato.

Mi aspetto che il numero totale di righe sarà tra le migliaia basse.

Inoltre, il motivo nvarchar (2048)è che il valore deve imitarlo in un DB di terze parti. Un tipico esempio sarà qualcosa di simile

\Translation Providers\[customer name]\[order name]\

e può contenere segni diacritici.

Quindi la mia domanda è questa: sarebbe più efficiente aggiungere un idcampo di incremento automatico e usarlo insieme company_idcome chiave primaria, o aggiungerebbe un sovraccarico non necessario - e il fatto che company_idsia la chiave primaria in un'altra tabella ha qualche effetto qui?

Risposte:


7

Per un indice cluster non univoco da comany_idsolo, SQL Server aggiungerà automaticamente un unificatore univoco a 4 byte interi a tutte le chiavi di indice cluster duplicate (ovvero il secondo e successivo per un valore chiave) per renderlo univoco. Tuttavia, ciò non è esposto all'utente.

Il vantaggio di aggiungere il tuo identificatore univoco come colonna chiave secondaria è che puoi ancora cercare company_idma anche cercare le singole righe in modo più efficiente (usando company_id, identitycolpiuttosto che company_idcon un predicato residuo su org_path). L'indice cluster sarebbe quindi univoco su company_id, identitycol, quindi non verranno aggiunti univoci unificatori nascosti.

Inoltre, se si (company_id,org_path)ottengono duplicati per , avere la colonna di identità esplicita (una sorta di "unificatore univoco esposto") renderà più semplice il targeting di uno solo di essi per l'eliminazione o l'aggiornamento.


12

Una cosa da considerare è che una chiave primaria e un indice cluster non sono la stessa cosa. Una chiave primaria è un vincolo e si occupa delle regole in base alle quali i dati vivono (ovvero l'integrità dei dati); non ha nulla a che fare con l'efficienza / le prestazioni. Una chiave primaria richiede che le colonne chiave siano univoche (in combinazione) e NON NULL (singolarmente). Un PK viene applicato tramite un indice univoco, sebbene possa essere cluster o non cluster.

Un indice cluster è un mezzo per ordinare fisicamente (cioè su disco) i dati nella tabella e gestire le prestazioni; non ha nulla a che fare con l'integrità dei dati. Un indice cluster puòrichiedono che le colonne chiave siano univoche (in combinazione), ma non è necessario. Tuttavia, poiché l'indice cluster è l'ordine fisico dei dati, è necessario identificare in modo univoco ogni riga, qualunque cosa accada. Pertanto, se non lo si imposta per richiedere l'univocità, creerà la propria unicità tramite una colonna "uniquifier" a 4 byte nascosta. Quella colonna è sempre presente negli indici cluster non univoci, ma non occupa spazio quando i campi chiave sono univoci (in combinazione). Per vedere in prima persona come funziona questa colonna "uniquifier" (sia nell'indice cluster sia l'effetto sugli indici non cluster), dai un'occhiata a questo script di test che ho pubblicato su PasteBin: script T-SQL per testare le dimensioni di Uniquifier .

Quindi, la domanda principale di:

sarebbe più efficiente aggiungere un idcampo di incremento automatico e usarlo insieme company_idcome chiave primaria, o aggiungerebbe un sovraccarico non necessario

sta combinando questi due concetti, quindi devono essere affrontati separatamente, anche se c'è sicuramente qualche sovrapposizione.

Dovrebbe IDENTITYessere aggiunta una colonna o sarebbe un sovraccarico non necessario?

Se aggiungi una INT IDENTITYcolonna e la usi per creare un PK, supponendo che sarebbe un PK in cluster, che aggiunge 4 byte a ogni riga. Questa colonna è visibile e utilizzabile nelle query. Si potrebbe essere aggiunto ad altri tavoli come chiave esterna, anche se in questo caso particolare che non accadrà.

Se non aggiungi la INT IDENTITYcolonna, non puoi creare un PK su questa tabella. Tuttavia, è ancora possibile creare un indice cluster sulla tabella purché non si utilizzi l' UNIQUEopzione. In questo caso, SQL Server aggiungerà una colonna nascosta chiamata "uniquifier" che si comporta come descritto sopra. Poiché la colonna è nascosta, non può essere utilizzata nelle query o come riferimento per le chiavi esterne.

Per quanto riguarda l'efficienza, queste opzioni sono all'incirca le stesse. Sì, ci sarà un po ' meno spazio occupato dall'indice cluster non univoco a causa di alcune righe (quelle con i valori chiave univoci iniziali) che occupano 0 byte mentre tutte le righe in IDENTITY/ PK occuperanno i 4 byte. Ma non ci sarà abbastanza delle righe a 0 byte (specialmente con la piccola quantità di righe previste) per notare mai una differenza, per non parlare della comodità di poter usare la IDcolonna nelle query.

INT IDENTITY Colonna o hash della org_pathcolonna calcolata persistente?

Dato che non cercherai le righe in base ai org_pathvalori, non ha senso aggiungere l'overhead della colonna calcolata persistente oltre a dover calcolare l'hash nelle query per corrispondere alla colonna calcolata (questo era il mio suggerimento originale, disponibile nella cronologia delle revisioni qui , basato sulla formulazione iniziale / dettagli della domanda). In questo caso particolare, la INT IDENTITYcolonna "ID" è probabilmente la migliore.

Ordine colonna chiave

Dato che la IDcolonna verrà raramente, se mai, utilizzata nelle query e dato che i due casi d'uso principali devono ottenere "tutte le righe" o "tutte le righe per un dato company_id", creerei il PK company_id, id. E poiché ciò significa che le righe non vengono inserite in sequenza, specificherei un valore FILLFACTORdi 90. Dovrai inoltre assicurarti di eseguire regolarmente la manutenzione dell'indice per ridurre la frammentazione.

Seconda domanda

il fatto che company_id sia la chiave primaria in un'altra tabella ha qualche effetto qui

No.

grilletto

Poiché i org_pathvalori all'interno di a company_idsono univoci, è comunque necessario creare un trigger INSERT, UPDATEper imporlo. Nel trigger, eseguire un IF EXISTScon una query che probabilmente esegue un COUNT(*)e GROUP BY company_id, org_path. Se viene trovato qualcosa, emettere a ROLLBACKper annullare l'operazione DML e quindi RAISERRORdire che ci sono duplicati.

confronto

Nella mia risposta iniziale (basata sulla formulazione originale / dettagli sparsi della domanda, e disponibile nella cronologia delle revisioni qui ), avevo suggerito possibilmente di usare una raccolta binaria (cioè _BIN2). Ora che abbiamo una visione di cosa sia esattamente org_path, non consiglierei di usare una raccolta binaria. Dal momento che non ci saranno segni diacritici, è cosa vuole fare uso di equivalenze linguistiche.



0

Perché hai bisogno di un PK?

Perché non andare semplicemente con company_id come indice non cluster?

Hai detto che i più cercati sono su tutte le voci o da company_id
Raramente aggiorna
Raramente elimina
org_path, questa è l'unica tabella in cui esiste

La risposta di Martin Smith potrebbe darti ciò di cui hai bisogno
Non ho familiarità con l'aggiunta automatica di un unificatore univoco a 4 byte
Forse mi sto perdendo qualcosa, ma se non hai altre colonne indicizzate, non vedo alcun motivo in questo caso d'uso

Se sei preoccupato per il DRI, le tabelle dovrebbero usare la tabella Company come FK per company_id


Hey. Per quanto riguarda " Perché non limitarsi a utilizzare company_id come indice non cluster? ": Perché ciò avrebbe 2 lati negativi : 1) sarebbe 1 altra cosa che occupa spazio mentre un indice cluster è la tabella, quindi nessun elemento aggiuntivo e 2) richiederebbe comunque una ricerca RID per ottenere il campo NVARCHAR, a meno che non fosse una INCLUDEcolonna, ma è anche peggio perché sta semplicemente duplicando la tabella. È vero, il PK non è necessario; la parte importante è l'indice cluster. Ma una volta che hai l'IDENTITÀ, potrebbe anche andare con PK. E vedi il nuovo link nella mia risposta per una guida su Uniquifier 😃
Solomon Rutzky,

@srutzky Ma evita un unificatore univoco di 4 byte, quindi lo vedo come un lavaggio
paparazzo

Con meno di 10k righe, non importa; probabilmente dovrai essere in milioni di righe prima di notare l'effetto di soli 4 byte. Quindi per la query "ottieni tutte le righe" non c'è davvero alcuna differenza in nessuna di queste opzioni. Ma per la query "get for company_id = @param", avere i dati fisicamente ordinati da company_id aiuterà, specialmente quando non è necessario eseguire una ricerca RID per ogni riga.
Solomon Rutzky,

@srutzky Wash è un lavaggio - 10K o 1G. È solo qualcosa da considerare per l'OP.
paparazzo,
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.