it-swarm.com.de

NVARCHAR-Spalte als PRIMARY KEY oder als UNIQUE-Spalte

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 Spalte [CODE] Als Primärschlüssel verwenden und die Spalte [ID_CODE] Entfernen.

Gibt es ein Problem oder eine Strafe, wenn ich eine NVARCHAR Spalte als PRIMARY KEY Habe?

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

Muss ich [CODE] Als Primärschlüssel verwenden oder ist es besser, wenn ich eine UNIQUE -Einschränkung für die Spalte [CODE] Festlege?

11
VansFannel

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 Auswirkungen der Verwendung eines Zeichenfolgenfelds sehen, hängt jedoch davon ab, a) wie viele Zeilen in dieser Tabelle enthalten sind 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 nichts zu befürchten sind, lesen Sie bitte meinen Artikel Disk Is Cheap! ORLY? Welche Details alle (oder zumindest die meisten) ) der von dieser Entscheidung betroffenen Bereiche.

Die andere zu berücksichtigende Sache ist, dass das Filtern und Sortieren nach Zeichenfolgen, insbesondere wenn keine binäre Sortierung 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 wäre die Verwendung von CHAR(5) 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 ON UPDATE CASCADE Setzen). 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 UNIQUE INDEX ( was bedeutet, dass es sich um einen "alternativen Schlüssel" handelt).


Update
Ein wenig 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 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 funktioniert, 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 für Clustered-Indizes ziemlich schlecht (NEWSEQUENTIALID ist 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 Feld [CODE] 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:

  • In Bezug auf den Wert [CODE]:

    • 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 ist, das zum Abrufen einzelner Zeilen verwendet wird, welchen Zweck erfüllt das Feld [ID_CODE]? Wenn es nicht verwendet wird, warum sollte es überhaupt verwendet werden (was von der Antwort auf "Kann sich das Feld [CODE] Jemals ändern?" Abhängig sein könnte)?
    • 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 Fällen kann es also in Ordnung sein, [CODE] Als 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 das Feld [ID_CODE] Als PK wählen, selbst wenn es nicht gruppiert ist.

13
Solomon Rutzky

Sie müssen die Konzepte trennen:

  • Primärschlüssel ist ein Design Konzept, 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 Speicher Konzept, 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_CODE Als PK und (CODE_LEVEL, CODE) Als Clusterschlüssel verwenden. Oder umgekehrt.

Ein größerer gruppierter Schlüssel hat einige negative Auswirkungen, da der breitere Schlüssel eine geringere Dichte auf den Indexseiten und eine größere Größe bedeutet, die auf allen nicht gruppierten Indizes verbraucht wird. 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 IO Bandbreite ist wahrscheinlich the 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 ist meine 2c: NVARCHAR(20) nicht breit. Ist eine durchaus akzeptable Clusterschlüsselgröße, selbst für eine große Tabelle.

6
Remus Rusanu

Ich würde niemals zulassen, dass jemand eine nvarchar(20) zu einer PK in meiner Datenbank macht. 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. Welche Art von Daten möchten Sie in CODE speichern? 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. Die angezeigten 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 erstellen, reicht dann nicht ein normales int identity (1,1) mit einem Limit von über 2 Milliarden aus?

Es sollte keine inhärente/spürbare Strafe geben, außer dass Sie das Risiko eingehen, breite Schlüssel 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 selbst erstellt.)

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

3
Allan S. Hansen