NVARCHAR-Spalte als PRIMARY KEY oder als UNIQUE-Spalte

11

Ich entwickle eine SQL Server 2012-Datenbank und habe Zweifel an nvarchar-Spalten als Primärschlüssel.

Ich habe diese Tabelle:

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
    )
)

Aber jetzt möchte ich die [CODE]Spalte als Primärschlüssel verwenden und die [ID_CODE]Spalte entfernen .

Gibt es ein Problem oder eine Strafe, wenn ich eine NVARCHARSpalte als habe PRIMARY KEY?

[CODE]Der Spaltenwert muss eindeutig sein, daher habe ich gedacht, dass ich eine UNIQUEEinschränkung für diese Spalte festlegen kann .

Muss ich [CODE]als Primärschlüssel verwenden oder ist es besser, wenn ich eine UNIQUEEinschränkung für die [CODE]Spalte festlege ?

VansFannel
quelle
1
Ganz wichtig ist, wie viele Zeilen sich in Ihrer Tabelle befinden.
James Z
Dies ist keine Antwort per se , aber ich neige dazu zu denken , dass Ihre CODESpalte eindeutig sein sollte, aber keinen Primärschlüssel. Ich vermute, dass es Informationen enthält. Wenn diese Informationen in irgendeiner Weise CODEgeändert werden können, sollten Sie sich ändern oder veraltet sein. Das würde Ihren Primärschlüssel flüchtig machen, und ich kann nicht sehen, dass das gut endet. Lassen Sie Ihre PK am besten nur ein Schlüssel sein, und Ihr CODE kann tun, was er will. Nur eine Meinung.
Manngo
@ Manngo, danke für deinen Kommentar. Ja, ich habe es so gemacht: ID_CODE ist der Primärschlüssel und CODE ist EINZIGARTIG.
VansFannel

Antworten:

13

Ja, es gibt absolut negative Konsequenzen für die Verwendung einer Zeichenfolge anstelle eines numerischen Typs für einen Primärschlüssel, und dies umso mehr, wenn diese PK geclustert ist (was in Ihrem Fall tatsächlich der Fall ist). Der Grad, in dem Sie die Auswirkung (en) der Verwendung eines Zeichenfolgenfelds sehen, hängt jedoch davon ab, a) wie viele Zeilen sich in dieser Tabelle befinden und b) wie viele Zeilen in anderen Tabellen für diese PK mit einem Fremdschlüssel versehen sind. Wenn Sie nur 10.000 Zeilen in dieser Tabelle und 100.000 Zeilen in einigen anderen Tabellen haben, die über dieses Feld an diese Tabelle weitergeleitet werden, ist dies möglicherweise nicht so auffällig. Aber diese Effekte werden mit zunehmender Anzahl der Zeilen sicherlich deutlicher.

Sie müssen berücksichtigen, dass die Felder in einem Clustered-Index auf Nicht-Clustered-Indizes übertragen werden. Sie betrachten also nicht nur bis zu 40 Bytes pro Zeile, sondern (40 * some_number) Bytes. Und in allen FK-Tabellen haben Sie dieselben 40 Bytes in der Zeile. In den meisten Fällen gibt es einen nicht gruppierten Index für dieses Feld, wie er in JOINs verwendet wird. In allen Tabellen, in denen FK verwendet wird, wird er jetzt wirklich verdoppelt dieses. Wenn man denkt, dass 40 Bytes * 1 Million Zeilen * 10 Kopien davon kein Grund zur Sorge sind, lesen Sie bitte meinen Artikel Disk Is Cheap! ORLY? Darin werden alle (oder zumindest die meisten) Bereiche aufgeführt, die von dieser Entscheidung betroffen sind.

Die andere zu berücksichtigende Sache ist, dass das Filtern und Sortieren nach Zeichenfolgen, insbesondere wenn keine binäre Kollatierung verwendet wird (ich gehe davon aus, dass Sie den Datenbankstandard verwenden, bei dem die Groß- und Kleinschreibung normalerweise nicht berücksichtigt wird), weitaus weniger effizient ist (dh länger dauert) als bei Verwendung von INT/ BIGINT. Dies wirkt sich auf alle Abfragen aus, die in diesem Feld gefiltert / verknüpft / sortiert werden.

Daher CHAR(5)wäre die Verwendung von so etwas für eine Clustered PK wahrscheinlich in Ordnung, aber meistens, wenn es auch mit COLLATE Latin1_General_100_BIN2(oder so ähnlich) definiert wurde.

Und kann sich der Wert von [CODE]jemals ändern? Wenn ja, dann ist das noch mehr Grund, es nicht als PK zu verwenden (selbst wenn Sie die FKs auf setzen ON UPDATE CASCADE). Wenn es sich nicht ändern kann oder wird, ist das in Ordnung, aber es gibt bereits mehr als genug Gründe, es nicht als Clustered PK zu verwenden.

Natürlich könnte die Frage falsch formuliert sein, da es den Anschein hat, dass Sie dieses Feld derzeit bereits in Ihrer PK haben.

Unabhängig davon ist es bei weitem die beste Option, [ID_CODE]als Clustered PK zu verwenden, dieses Feld in verwandten Tabellen als FK zu verwenden und [CODE]als zu behalten UNIQUE INDEX(was bedeutet, dass es sich um einen "alternativen Schlüssel" handelt).


Update
Ein bisschen mehr Infos basierend auf dieser Frage in einem Kommentar zu dieser Antwort:

Ist [ID_CODE] als PRIMARY KEY die beste Option, wenn ich die Spalte [CODE] zum Nachschlagen der Tabelle verwende?

Dies alles hängt von sehr vielen Faktoren ab, von denen ich einige bereits erwähnt habe, aber noch einmal wiederholen werde:

Ein Primärschlüssel gibt an, wie die einzelne Zeile identifiziert wird, unabhängig davon, ob sie von einem Fremdschlüssel referenziert wird oder nicht. Wie Ihr System die Zeile intern identifiziert, hängt damit zusammen, aber nicht unbedingt mit der Art und Weise, wie Ihre Benutzer sich selbst / diese Zeile identifizieren. Jede NOT NULL-Spalte mit eindeutigen Daten könnte funktionieren, es sind jedoch praktische Aspekte zu berücksichtigen, insbesondere wenn die PK tatsächlich von FKs referenziert wird. Zum Beispiel sind GUIDs einzigartig und einige Leute verwenden sie aus verschiedenen Gründen sehr gerne, aber sie sind ziemlich schlecht für Clustered-Indizes ( NEWSEQUENTIALIDist besser, aber nicht perfekt). Auf der anderen Seite sind GUIDs als alternative Schlüssel in Ordnung und werden von der App zum Nachschlagen der Zeile verwendet. Die JOINs werden jedoch weiterhin mit einer INT-PK (oder einer ähnlichen PK) ausgeführt.

Bisher haben Sie uns nicht gesagt, wie das [CODE]Feld aus allen Blickwinkeln in das System passt, außer jetzt zu erwähnen, dass Sie auf diese Weise Zeilen nachschlagen, aber ist das für alle Abfragen oder nur für einige? Daher:

  • Zum [CODE]Wert:

    • Wie wird es erzeugt?
    • Ist es inkrementell oder pseudozufällig?
    • Ist es eine einheitliche Länge oder eine unterschiedliche Länge?
    • Welche Zeichen werden verwendet?
    • Wenn Sie alphabetische Zeichen verwenden: Wird zwischen Groß- und Kleinschreibung unterschieden oder nicht?
    • Kann es sich nach dem Einfügen jemals ändern?
  • Zu dieser Tabelle:

    • Haben andere Tabellen FK zu dieser Tabelle? Oder werden diese Felder ( [CODE]oder [ID_CODE]) in anderen Tabellen verwendet, auch wenn sie nicht explizit mit Fremdschlüssel versehen sind?
    • Wenn [CODE] das einzige Feld zum Abrufen einzelner Zeilen verwendet wird, welchen Zweck erfüllt das [ID_CODE]Feld dann? Wenn es nicht verwendet wird, warum sollte es überhaupt verwendet werden (was von der Antwort auf "Kann sich das [CODE]Feld jemals ändern?" Hängt )?
    • Wie viele Zeilen in dieser Tabelle?
    • Wenn andere Tabellen auf diese Tabelle verweisen, wie viele und wie viele Zeilen in jeder von ihnen?
    • Was sind die Indizes für diese Tabelle?

Diese Entscheidung kann nicht nur über die Frage "NVARCHAR ja oder nein?" Ich werde noch einmal sagen, dass ich es im Allgemeinen nicht für eine gute Idee halte, aber es gibt sicherlich Zeiten, in denen es in Ordnung ist. Bei so wenigen Feldern in dieser Tabelle ist es unwahrscheinlich, dass es mehr oder zumindest nicht viele Indizes gibt. In beiden [CODE]Fällen kann es also in Ordnung sein, den Clustered-Index zu verwenden. Und wenn keine anderen Tabellen auf diese Tabelle verweisen, ist es möglicherweise auch in Ordnung, sie zur PK zu machen. Wenn jedoch andere Tabellen auf diese Tabelle verweisen, würde ich mich für das [ID_CODE]Feld als PK entscheiden, selbst wenn es nicht gruppiert ist.

Solomon Rutzky
quelle
Würde der anonyme Downvoter (der anscheinend auch die Antwort von @noIDonthissystem abgelehnt hat) konstruktive Kritik üben oder auf eine fehlerhafte Logik hinweisen wollen?
Solomon Rutzky
Danke für deine Antwort. Ist [ID_CODE]as PRIMARY KEYdie beste Option, wenn ich [CODE]die Tabelle zum Nachschlagen der Tabelle mit einer Spalte verwende?
VansFannel
@VansFannel siehe mein Update. Vielen Dank.
Solomon Rutzky
Ich bin dieser dba-Community beigetreten, um diese Antwort zu verbessern.
Ahmet Arslan
6

Sie müssen die Konzepte trennen:

  • Primärschlüssel ist ein Entwurfskonzept , eine logische Eigenschaft der Einträge in der Tabelle. Es sollte während der Lebensdauer des Tabelleneintrags unveränderlich sein und der Schlüssel sein, der in der Anwendung zum Verweisen auf den Eintrag verwendet wird.

  • Clustered Index ist ein Speicherkonzept , eine physikalische Eigenschaft. Es sollte der häufigste Zugriffspfad für Abfragen sein, in den meisten Fällen als Deckungsindex dienen und so viele Bereichsabfragen wie möglich erfüllen.

Ist nicht erforderlich, damit der Primärschlüssel der Clustered-Index ist. Sie können ID_CODEals PK und (CODE_LEVEL, CODE)als Clustered Key haben. Oder umgekehrt.

Ein größerer Clustered Key hat einige negative Auswirkungen, da der breitere Key eine geringere Dichte auf den Indexseiten und eine größere Größe aller nicht Clustered Indizes bedeutet. Zu diesem Thema wurden bereits Tonnen Tinte verschüttet, z. Beginnen Sie mit Weitere Überlegungen zum Clustering-Schlüssel - die Debatte über den Clustered-Index wird fortgesetzt! .

Der Kern der Sache ist jedoch, dass die Wahl des Clustered-Index-Schlüssels in erster Linie ein Kompromiss ist. Einerseits haben Sie Anforderungen an die Speichergröße mit allgemeinen Auswirkungen auf die Leistung (größerer Schlüssel -> größere Größe -> mehr E / A und E / A-Bandbreite ist wahrscheinlich die knappste Ressource, die Sie haben). Andererseits kann die Auswahl des falschen Clusterschlüssels im Namen der Platzersparnis Auswirkungen auf die Abfrageleistung haben, die häufig schlimmer sind als die Probleme, die sich aus einem breiten Schlüssel ergeben.

Die Wahl des Primärschlüssels sollte nicht einmal ein Problem sein: Ihr Datenmodell, Ihre App-Logik, sollte den Primärschlüssel bestimmen.

Davon abgesehen NVARCHAR(20)ist mein 2c: nicht breit. Ist eine absolut akzeptable Clusterschlüsselgröße, selbst für eine große Tabelle.

Remus Rusanu
quelle
Danke für deine Antwort. Ist [ID_CODE]as PRIMARY KEYdie beste Option, wenn ich [CODE]Spalte (und vielleicht [CODE_LEVEL]) verwende, um die Tabelle nachzuschlagen?
VansFannel
@VansFannel nur Sie können das beantworten.
Remus Rusanu
Aber Ihrer Meinung nach ...
VansFannel
2
Meiner Meinung nach müsste die genaue DDL der gesamten Tabelle und aller Indizes, die darauf verweisenden Fremdschlüssel, die geschätzte Anzahl der Zeilen, die erwartete Abfragearbeitslast, die erwarteten SLAs der Anwendung und nicht zuletzt die verfügbaren Ressourcen für Hardware und Lizenzierung berücksichtigt werden.
Remus Rusanu
Vielen Dank. Ich werde die [CODE]Spalte als Primärschlüssel verwenden.
VansFannel
4

Ich würde niemals zulassen, dass jemand nvarchar(20)eine PK in meiner Datenbank erstellt. Sie verschwenden Speicherplatz und Cache-Speicher. Jeder Index in dieser Tabelle und alle dazugehörigen FKs replizieren diesen breiten Wert. Vielleicht ein Zeichen (20), wenn sie es rechtfertigen können. In welcher Art von Daten möchten Sie speichern CODE? Müssen Sie wirklich nvarchar-Zeichen speichern? Ich neige dazu, PKs "interne" Werte zu machen, die von den Benutzern nicht gesehen werden, und ich versuche, Werte, die angezeigt werden, getrennt zu halten. Angezeigte Werte müssen manchmal geändert werden, was bei PKs + FKs sehr problematisch wird.

Ist Ihnen auch klar, dass eine 'Bigint-Identität (1,1)' bis zu 9.223.372.036.854.775.807 erhöhen kann?

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

Wenn Sie diese Datenbank nicht für Google int identity (1,1)erstellen, reicht ein Normalwert mit einem Limit von über 2 Milliarden nicht aus?

Keine ID auf diesem System
quelle
int ist 4 Byte in SQL, was Ihnen -2,1 Milliarden bis + 2,1 Milliarden ergibt.
Datum
@datagod, ha danke, so viele Ziffern habe ich falsch gezählt!
Keine ID auf diesem System
Danke für deine Antwort. Ist [ID_CODE]as PRIMARY KEYdie beste Option, wenn ich [CODE]die Tabelle zum Nachschlagen der Tabelle mit einer Spalte verwende? Vielen Dank.
VansFannel
Ich war in diesem Boot, bis jemand die sequentielle Natur von "int" verwendete, um Daten / Benutzer in meiner Datenbank vorherzusagen, und fast alles erntete, was ich hatte. Nie wieder. Öffentliche DBs müssen etwas schwieriger sein, um Informationen zu erhalten.
DaBlue
3

Es sollte keine inhärente / spürbare Strafe geben, außer dass Sie das Risiko eingehen, breite Tasten zu verwenden, wenn Sie nvarchar / varchar verwenden, wenn Sie dies nicht wissen. Vor allem, wenn Sie sie in zusammengesetzten Schlüsseln kombinieren.

Aber in Ihrem Beispiel einer (20) Länge sollte es Ihnen gut gehen, und ich würde mir darüber keine großen Sorgen machen. Denn wenn Sie mit CODE hauptsächlich Ihre Daten abfragen, klingt ein Clustered-Index sehr sinnvoll.

Sie sollten jedoch überlegen, ob Sie es tatsächlich als Primärschlüssel oder nur als eindeutigen (gruppierten) Index möchten. Es gibt einen (kleinen) Unterschied zwischen dem Clustered-Index und dem Primärschlüssel (im Grunde genommen identifiziert der Primärschlüssel Ihre Daten, aber der Index gibt an, wie Sie Daten abfragen). Wenn Sie möchten, können Sie Ihren ID_Code also genauso einfach wie einen Primärschlüssel erstellen und Erstellen Sie einen eindeutigen Clustered-Index über CODE. (Hinweis: SQL Server verwandelt Ihren Primärschlüssel automatisch in einen Clustered-Index, es sei denn, Sie haben den Clustered-Index manuell erstellt.)

Überlegen Sie auch, ob Sie ID_Code tatsächlich benötigen, jetzt haben Sie einen eindeutigen CODE.

Allan S. Hansen
quelle
2
Tatsächlich hat NVARCHAR(20)es eine Größe von maximal 40 Byte, und da es sich um eine Spalte mit variabler Länge handelt , ist es nicht wirklich die beste Wahl für einen Clustered-Index. ID_CODEein zu sein BIGINT IDENTITYwäre hier die viel bessere Wahl!
marc_s
Ich weiß, dass es 40 Bytes sind, aber es gab nicht viel Grund, es anzugeben, da es nicht annähernd 900 Bytes sind. Und wenn Sie hauptsächlich die Daten von CODE abfragen, ist es eine bessere Wahl, zu vermeiden, dass redundante Indizes verwaltet werden müssen, da Sie immer noch einen Index benötigen und dann nach dem Cluster suchen müssen
Allan S. Hansen
Erwähnenswert - was ich vergessen habe zu erwähnen und was ich vermute, ist, dass @marc_s anspricht, dass ein Index dieses Typs zu einer größeren Indexfragmentierung führen kann als eine sequentielle Identität, aber ich sehe ihn in dieser speziellen Situation immer noch als vernünftigen Index auf den Abfragefaktor.
Allan S. Hansen