Colonna NVARCHAR come PRIMARY KEY o come colonna UNIQUE


11

Sto sviluppando un database SQL Server 2012 e ho dei dubbi sulle colonne nvarchar come chiavi primarie.

Ho questa tabella:

CREATE TABLE [dbo].[CODES]
(
    [ID_CODE] [bigint] IDENTITY(1,1) NOT NULL,
    [CODE_LEVEL] [tinyint] NOT NULL,
    [CODE] [nvarchar](20) NOT NULL,
    [FLAG] [tinyint] NOT NULL,
    [IS_TRANSMITTED] [bit] NOT NULL DEFAULT 0,
     CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED 
    (
        [CODE_LEVEL] ASC,
        [CODE] ASC
    )
)

Ma ora voglio usare la [CODE]colonna come chiave primaria e rimuovere la [ID_CODE]colonna.

C'è qualche problema o penalità se ho una NVARCHARcolonna come PRIMARY KEY?

[CODE]il valore della colonna deve essere univoco, quindi ho pensato di poter impostare un UNIQUEvincolo per quella colonna.

Devo usare [CODE]come chiave primaria o è meglio se imposto un UNIQUEvincolo sulla [CODE]colonna?


1
Una cosa abbastanza importante in considerazione è quante righe ci saranno nella tua tabella?
James Z,

Questa non è una risposta in , ma sono propenso a pensare che la tua CODEcolonna debba essere unica, ma non una chiave primaria. Ho il sospetto che trasporta informazioni. Se tali informazioni sono in qualche modo modificabili, è CODEnecessario modificarle o non essere aggiornate. Ciò renderebbe volatile la tua chiave primaria e non riesco a vederla bene. È meglio lasciare che il tuo PK sia solo una chiave e il tuo CODICE può fare quello che gli piace. Solo un'opinione.
Manngo,

@Manngo, grazie per il tuo commento. Sì, l'ho fatto in questo modo: ID_CODE è la chiave primaria e CODE è UNICO.
VansFannel

Risposte:


13

Sì, ci sono assolutamente conseguenze negative sull'uso di una stringa anziché di un tipo numerico per una chiave primaria, e ancora di più se quel PK è cluster (cosa che in effetti è nel tuo caso). Tuttavia, il grado in cui vedi l'effetto (i) dell'uso di un campo stringa è una funzione di a) quante righe sono in questa tabella eb) quante righe in altre tabelle sono con chiave esterna a questo PK. Se hai solo 10k righe in questa tabella e 100k righe in poche altre tabelle che vengono indirizzate a questa tabella tramite quel campo, forse non sarà così evidente. Ma questi effetti diventano certamente più evidenti all'aumentare del numero di righe.

È necessario considerare che i campi in un indice cluster vengono trasferiti in indici non cluster. Quindi non stai solo guardando fino a 40 byte per riga, ma (40 * un certo numero) byte. E in tutte le tabelle FK hai gli stessi 40 byte nella riga più il più delle volte ci sarà un indice non cluster su quel campo come viene utilizzato nei JOIN, quindi ora è davvero raddoppiato in tutte le tabelle che FK questo. Se si è inclini a pensare che 40 byte * 1 milione di righe * 10 copie di esso non siano nulla di cui preoccuparsi, consultare il mio articolo Disk Is Cheap! ORLY? che descrive in dettaglio (o almeno la maggior parte) delle aree interessate dalla presente decisione.

L'altra cosa da considerare è che il filtraggio e l'ordinamento su stringhe, specialmente quando non si utilizza un confronto binario (suppongo che si stia utilizzando l'impostazione predefinita del database che in genere non fa distinzione tra maiuscole e minuscole) è molto meno efficiente (ovvero richiede più tempo) rispetto a quando si utilizza INT/ BIGINT. Ciò influisce su tutte le query che filtrano / uniscono / ordinano su questo campo.

Quindi, usare qualcosa del genere CHAR(5)sarebbe probabilmente OK per un PK in cluster, ma soprattutto se fosse anche definito con COLLATE Latin1_General_100_BIN2(o qualcosa del genere).

E il valore di [CODE]mai cambiare? Se sì, questo è un motivo in più per non usarlo come PK (anche se si impostano gli FK su ON UPDATE CASCADE). Se non può o non cambierà mai, va bene, ma ci sono ancora ragioni più che sufficienti per non usarlo come PK in cluster.

Naturalmente, la domanda potrebbe essere formulata in modo errato in quanto sembra che tu abbia già questo campo nel tuo PK.

Indipendentemente da ciò, l'opzione migliore, di gran lunga, è utilizzare [ID_CODE]come PK in cluster, utilizzare quel campo in tabelle correlate come FK e mantenere [CODE]come un UNIQUE INDEX(che significa che è una "chiave alternativa").


Aggiorna Altre
informazioni basate su questa domanda in un commento su questa risposta:

[ID_CODE], come PRIMARY KEY, è l'opzione migliore se utilizzo la colonna [CODICE] per cercare la tabella?

Tutto dipende da moltissimi fattori, alcuni dei quali ho già menzionato ma ribadirò:

Una chiave primaria è il modo in cui viene identificata la singola riga, indipendentemente dal fatto che sia referenziata o meno da qualsiasi chiave esterna. Il modo in cui il sistema identifica internamente la riga è correlato, ma non necessariamente lo stesso, al modo in cui gli utenti identificano se stessi / quella riga. Qualsiasi colonna NOT NULL con dati univoci potrebbe funzionare, ma ci sono problemi di praticità da considerare, specialmente se il PK è, di fatto, referenziato da qualsiasi FK. Ad esempio, i GUID sono unici e ad alcune persone piace molto usarli per vari motivi, ma sono piuttosto dannosi per gli indici cluster ( NEWSEQUENTIALIDè meglio, ma non perfetto). D'altra parte, i GUID vanno bene come tasti alternativi e vengono utilizzati dall'app per cercare la riga, ma i JOIN vengono comunque eseguiti utilizzando un PK INT (o simile).

Finora non ci hai detto come il [CODE]campo si adatta al sistema da tutte le angolazioni, al di fuori di ora menzionando che è così che cerchi le righe, ma lo è per tutte le domande o solo alcune? Quindi:

  • Per quanto riguarda il [CODE]valore:

    • Come viene generato?
    • È incrementale o psue-casuale?
    • È lunghezza uniforme o lunghezza variabile?
    • Quali personaggi vengono usati?
    • Se si usano caratteri alfabetici: è sensibile al maiuscolo / minuscolo o non è sensibile?
    • Può mai cambiare dopo essere stato inserito?
  • Per quanto riguarda questa tabella:

    • Altre tabelle FK in questa tabella? Oppure questi campi ( [CODE]o [ID_CODE]) sono utilizzati in altre tabelle, anche se non esplicitamente con chiave esterna?
    • Se [CODE] viene utilizzato l'unico campo per ottenere singole righe, a che scopo serve il [ID_CODE]campo? Se non viene utilizzato, perché in primo luogo (che potrebbe dipendere dalla risposta a "Il [CODE]campo può mai cambiare?")?
    • Quante righe in questa tabella?
    • Se altre tabelle fanno riferimento a questa tabella, quante e quante righe in ciascuna di esse?
    • Quali sono gli indici per questa tabella?

Questa decisione non può essere presa esclusivamente sulla questione "NVARCHAR sì o no?". Dirò di nuovo che in generale non trovo che sia una buona idea, ma ci sono certamente momenti in cui va bene. Dati così pochi campi in questa tabella, è improbabile che ci siano altri o almeno non molti indici. Quindi potresti andare bene in entrambi i modi [CODE]come indice cluster. E se nessun'altra tabella fa riferimento a questa tabella, potresti anche andare bene trasformandola in PK. Ma, se altre tabelle fanno riferimento a questa tabella, opterei per il [ID_CODE]campo come PK, anche se non cluster.


Il downvoter anonimo (che sembra anche aver votato in negativo alla risposta di @noIDonthissystem) si preoccuperebbe di offrire critiche costruttive o di evidenziare qualche logica errata?
Solomon Rutzky,

Grazie per la tua risposta. È [ID_CODE], come PRIMARY KEY, l'opzione migliore se uso la [CODE]colonna per cercare la tabella?
VansFannel,

@VansFannel, per favore, vedi il mio aggiornamento. Grazie.
Solomon Rutzky,

Sono entrato a far parte di questa community di dba solo per votare questa risposta.
Ahmet Arslan,

6

Devi separare i concetti:

  • la chiave primaria è un concetto di design , una proprietà logica delle voci nella tabella. Dovrebbe essere immutabile durante la vita della voce della tabella e dovrebbe essere la chiave utilizzata nell'applicazione per fare riferimento alla voce.

  • l'indice cluster è un concetto di archiviazione , una proprietà fisica. Dovrebbe essere il percorso di accesso più comune per le query, dovrebbe servire a soddisfare come indice di copertura per la maggior parte dei casi e soddisfare il maggior numero possibile di query.

Non è necessario che la chiave primaria sia l'indice cluster. Puoi avere ID_CODEcome PK e (CODE_LEVEL, CODE)come chiave cluster. O viceversa.

Una chiave cluster più grande ha alcune ripercussioni negative, poiché la chiave più ampia significa una densità inferiore sulle pagine dell'indice e dimensioni maggiori consumate su tutti gli indici non cluster. su questo argomento sono già state versate tonnellate di inchiostro, ad es. iniziare da Altre considerazioni per la chiave di clustering - il dibattito sull'indice cluster continua! .

L'essenza della questione è che la scelta della chiave di indice cluster è principalmente un compromesso. Da un lato hai requisiti di dimensioni di archiviazione, con ripercussioni generali sulle prestazioni (chiave più grande -> dimensioni maggiori -> più IO e la larghezza di banda IO è probabilmente la risorsa più scarsa che hai). D'altra parte, la scelta della chiave cluster errata in nome del risparmio di spazio può avere conseguenze sulle prestazioni della query, spesso peggiori dei problemi derivanti da una chiave ampia.

Per quanto riguarda la scelta della chiave primaria, non dovrebbe nemmeno essere un problema: il modello di dati, la logica dell'app, dovrebbero dettare quale sia la chiave primaria.

Detto questo, il mio 2c: nonNVARCHAR(20) è ampio. È una dimensione della chiave cluster perfettamente accettabile, anche per una tabella di grandi dimensioni.


Grazie per la tua risposta. È [ID_CODE], come PRIMARY KEY, l'opzione migliore se uso la [CODE]colonna (e forse [CODE_LEVEL]) per cercare la tabella?
VansFannel,

@VansFannel solo tu puoi rispondere.
Remus Rusanu,

Ma secondo te ...
VansFannel,

2
La mia opinione dovrebbe considerare l'esatto DDL dell'intera tabella e di tutti gli indici, le chiavi esterne che lo fanno riferimento, il numero stimato di righe, il carico di lavoro delle query previsto, gli SLA previsti dell'applicazione e non ultimo il disponibile disponibile per hardware e licenze.
Remus Rusanu,

Grazie. Userò [CODE]colonna come chiave primaria.
VansFannel,

4

Non permetterei mai a nessuno di diventare nvarchar(20)un PK nel mio database. Sprechi spazio su disco e memoria cache. Ogni indice su questa tabella e tutti gli FK ad esso replicano questo ampio valore. Forse un personaggio (20) se possono giustificarlo. In che tipo di dati stai cercando di archiviare CODE? Hai davvero bisogno di conservare i personaggi di Nvarchar? Tendo a rendere i valori PK "interni" non visti dagli utenti e cerco di mantenere separati i valori visualizzati. I valori visualizzati a volte devono essere modificati, il che diventa molto problematico con PK + FK.

Inoltre, ti rendi conto che una "identità bigint (1,1)" può aumentare fino a 9.223.372.036.854.775.807?

[ID_CODE] [bigint] IDENTITY(1,1)

A meno che tu non stia costruendo questo database per Google, un normale int identity (1,1)con un limite di oltre 2 miliardi non sarà sufficiente?


int è 4 byte in SQL, che ti dà da -2,1 miliardi a + 2,1 miliardi.
datagod,

@datagod, ah grazie, così tante cifre ho contato male!
nessun ID su questo sistema il

Grazie per la tua risposta. È [ID_CODE], come PRIMARY KEY, l'opzione migliore se uso la [CODE]colonna per cercare la tabella? Grazie.
VansFannel,

Ero su questa barca fino a quando non ho avuto qualcuno che usa la natura sequenziale di "int" per prevedere i dati / utenti nel mio DB e ho raccolto quasi tutto ciò che avevo. Mai più. I DB di fronte al pubblico devono essere un po 'più difficili da ottenere informazioni.
DaBlue,

3

Non ci dovrebbero essere penalità intrinseche / evidenti se non il rischio di usare chiavi ampie quando si utilizza nvarchar / varchar se non si è consapevoli. Soprattutto se inizi a combinarli in tasti compositi.

Ma nel tuo esempio di lunghezza (20) dovresti stare bene e non me ne preoccuperei molto. Perché se CODICE è il modo in cui richiedi principalmente i tuoi dati, un indice cluster su questo suona molto sensato.

Tuttavia, è necessario considerare se lo si desidera effettivamente come chiave primaria o solo come indice univoco (cluster). C'è una (piccola) differenza tra l'indice cluster e la chiave primaria (in sostanza - la chiave primaria identifica i tuoi dati, ma l'indice è il modo in cui esegui la query dei dati), quindi se desideri puoi facilmente rendere il tuo ID_Code come chiave primaria e crea un indice cluster univoco su CODE. (avviso: SQL Server trasformerà automaticamente la tua chiave primaria in un indice cluster, a meno che tu non abbia creato manualmente l'indice cluster)

Valuta anche se hai effettivamente bisogno ID_Code ora che hai un CODICE univoco.


2
In realtà, NVARCHAR(20)è di 40 byte di dimensione (max), e dato che è di lunghezza variabile colonna, non è davvero la scelta migliore per un indice cluster. ID_CODEessere un BIGINT IDENTITYsarebbe la scelta molto migliore qui!
marc_s,

So che sono 40 byte, ma non c'erano molte ragioni per specificarlo, visto che non è vicino ai 900 byte. E se richiedi principalmente i dati da CODE, sarebbe una scelta migliore evitare di avere indici ridondanti da mantenere, perché avresti ancora bisogno di un indice su di esso, e quindi dovresti cercare attraverso il cluster a poppa
Allan S. Hansen,

Vale la pena ricordare - che ho dimenticato di menzionare e che sospetto sia il punto in cui @marc_s si sta rivolgendo è che un indice di questo tipo può portare a una frammentazione dell'indice maggiore di un'identità sequenziale, ma lo vedo ancora come un indice sensibile in questa situazione specifica basata sul fattore di interrogazione.
Allan S. Hansen,
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.