Sollte ich einer Querverweistabelle nur für PK-Zwecke ein Auto-Inkrement- / IDENTITY-Feld hinzufügen?

9

Ich füge meiner von SQL Server gehosteten Datenbank die folgende Querverweistabelle hinzu:

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

Das company_idFeld bezieht sich auf das idFeld in einer anderen Tabelle (in der es sich um den Primärschlüssel handelt).

Da es auch mehrere Datensätze mit demselben Datensatz geben kann company_id, müsste jeder Primärschlüssel beide Felder verwenden. Ich kann jedoch keinen Schlüssel mit beiden Feldern erstellen, da dies org_pathfür SQL Server zu lang ist.

Was org_pathist dies die Tabelle nur in der sie existiert. Es ist sehr wahrscheinlich, dass bei Abfragen in dieser Tabelle entweder alle Einträge oder alle org_pathEinträge von abgefragt werden company_id. Oder anders ausgedrückt, es sieht zweifelhaft aus, ob diese Tabelle jemals abgefragt wird org_path. Darüber hinaus ist es unwahrscheinlich, dass org_pathdiese aktualisiert und mit größerer Wahrscheinlichkeit eingefügt und - wahrscheinlich selten - gelöscht werden.

Ich gehe davon aus, dass die Gesamtzahl der Zeilen bei niedrigen Tausenden liegen wird.

Der Grund dafür nvarchar (2048)ist auch, dass der Wert den Wert in einer Datenbank eines Drittanbieters nachahmen muss. Ein typisches Beispiel wird so etwas sein

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

und kann diakritische Mittel enthalten.

Meine Frage lautet also: Wäre es effizienter, ein Auto-Inkrement- idFeld hinzuzufügen und dieses in Verbindung mit company_idals Primärschlüssel zu verwenden, oder würde es unnötigen Overhead hinzufügen - und hat die Tatsache, dass company_ides sich um den Primärschlüssel in einer anderen Tabelle handelt, welche Wirkung hier?

awj
quelle

Antworten:

7

Für einen nicht eindeutigen Clustered-Index comany_id allein fügt SQL Server allen doppelten (dh zweiten und nachfolgenden für einen Schlüsselwert) Clustered-Indexschlüsseln automatisch einen 4-Byte-Integer-Uniqueifier hinzu, um ihn eindeutig zu machen. Dies ist dem Benutzer jedoch nicht zugänglich.

Der Vorteil des Hinzufügens Ihrer eigenen eindeutigen Kennung als Sekundärschlüsselspalte besteht darin, dass Sie dann weiterhin company_idnach einzelnen Zeilen suchen können, aber auch effizienter danach suchen können ( company_id, identitycolanstatt company_idein Restprädikat zu verwenden org_path). Der Clustered-Index wäre dann eindeutig company_id, identitycol, sodass keine versteckten eindeutigen Kennungen hinzugefügt würden.

Wenn Sie am Ende Duplikate für haben (company_id,org_path), wird es durch die explizite Identitätsspalte (eine Art "exponierter Uniqueifier") einfacher, nur eines davon zum Löschen oder Aktualisieren zu finden.

Martin Smith
quelle
12

Eine zu berücksichtigende Sache ist, dass ein Primärschlüssel und ein Clustered-Index nicht dasselbe sind. Ein Primärschlüssel ist eine Einschränkung und behandelt die Regeln, nach denen die Daten leben (dh Datenintegrität). es hat nichts mit Effizienz / Leistung zu tun. Für einen Primärschlüssel müssen die Schlüsselspalten eindeutig (in Kombination) und NICHT NULL (einzeln) sein. Eine PK wird über einen eindeutigen Index erzwungen, kann jedoch entweder gruppiert oder nicht gruppiert sein.

Ein Clustered Index ist ein Mittel zum physischen (dh auf der Festplatte) Ordnen der Daten in der Tabelle und befasst sich mit der Leistung. Es hat nichts mit Datenintegrität zu tun. Ein Clustered Index kann müssen die Schlüsselspalten eindeutig sein (in Kombination), dies ist jedoch nicht erforderlich. Da der Clustered Index jedoch die physische Reihenfolge der Daten ist, muss jede Zeile unabhängig davon eindeutig identifiziert werden. Wenn Sie also nicht festlegen, dass Eindeutigkeit erforderlich ist, wird über eine versteckte 4-Byte-Spalte "Eindeutigkeit" eine eigene Eindeutigkeit erstellt. Diese Spalte befindet sich immer in nicht eindeutigen Clustered-Indizes, nimmt jedoch keinen Platz ein, wenn die Schlüsselfelder eindeutig sind (in Kombination). Um aus erster Hand zu sehen, wie diese "Eindeutigkeit" -Spalte funktioniert (sowohl im Clustered-Index als auch die Auswirkung auf nicht-Clustered-Indizes), T-SQL-Skript veröffentlicht habe, um die Uniquifier-Größe zu testen .

Daher die Hauptfrage von:

Wäre es effizienter, ein Auto-Inkrement- idFeld hinzuzufügen und dieses in Verbindung mit company_idals Primärschlüssel zu verwenden, oder würde es unnötigen Overhead hinzufügen

verschmilzt diese beiden Konzepte, so dass sie getrennt behandelt werden müssen, obwohl es definitiv einige Überschneidungen gibt.

Sollte eine IDENTITYSpalte hinzugefügt werden oder wäre dies unnötig?

Wenn Sie eine INT IDENTITYSpalte hinzufügen und damit eine PK erstellen, wird davon ausgegangen, dass es sich um eine Cluster-PK handelt, die jeder Zeile 4 Byte hinzufügt. Diese Spalte ist sichtbar und kann in Abfragen verwendet werden. Es könnte als Fremdschlüssel zu anderen Tabellen hinzugefügt werden, obwohl dies in diesem speziellen Fall nicht der Fall ist.

Wenn Sie die INT IDENTITYSpalte nicht hinzufügen , können Sie für diese Tabelle keine PK erstellen. Sie können jedoch weiterhin einen Clustered-Index für die Tabelle erstellen, solange Sie die UNIQUEOption nicht verwenden . In diesem Fall fügt SQL Server eine ausgeblendete Spalte mit dem Namen "Uniquifier" hinzu, die sich wie oben beschrieben verhält. Da die Spalte ausgeblendet ist, kann sie nicht in Abfragen oder als Referenz für Fremdschlüssel verwendet werden.

In Bezug auf die Effizienz sind diese Optionen ungefähr gleich. Ja, der nicht eindeutige Clustered-Index belegt etwas weniger Speicherplatz, da einige Zeilen (mit den anfänglichen eindeutigen Schlüsselwerten) 0 Byte belegen, während alle Zeilen in der IDENTITY/ PK die 4 Byte belegen. Es wird jedoch nicht genug von den 0-Byte-Zeilen geben (insbesondere bei der geringen erwarteten Anzahl von Zeilen), um jemals einen Unterschied zu bemerken, geschweige denn die Bequemlichkeit abzuwägen, die IDSpalte in Abfragen verwenden zu können.

INT IDENTITY-Spalte oder Hash einer persistierten berechneten org_pathSpalte?

Da Sie keine Zeilen basierend auf org_pathWerten suchen , ist es nicht sinnvoll, den Overhead der persistierten berechneten Spalte hinzuzufügen und diesen Hash in Abfragen zu berechnen, um mit der berechneten Spalte übereinzustimmen (dies war meine ursprünglicher Vorschlag, der im Revisionsverlauf hier verfügbar ist und auf dem ursprünglichen Wortlaut / den Einzelheiten der Frage basiert). In diesem speziellen Fall ist die INT IDENTITYSpalte "ID" wahrscheinlich am besten.

Reihenfolge der Schlüsselsäulen

Da die IDSpalte selten, wenn überhaupt, in Abfragen verwendet wird und die beiden Hauptanwendungsfälle darin bestehen, entweder "alle Zeilen" oder "alle Zeilen für eine bestimmte company_id" zu erhalten, würde ich die PK auf erstellen company_id, id. Und da dies bedeutet, dass Zeilen nicht nacheinander eingefügt werden, würde ich eine FILLFACTORvon 90 angeben . Sie müssen auch sicherstellen, dass eine regelmäßige Indexpflege durchgeführt wird, um die Fragmentierung zu verringern.

Zweite Frage

Hat die Tatsache, dass company_id der Primärschlüssel in einer anderen Tabelle ist, hier einen Einfluss?

Nein.

Auslösen

Da org_pathWerte innerhalb von a company_ideindeutig sind, sollten Sie dennoch einen Trigger erstellen INSERT, UPDATE, um dies zu erzwingen. Führen Sie im Trigger eine IF EXISTSmit einer Abfrage aus, die wahrscheinlich ein COUNT(*)und ausführt GROUP BY company_id, org_path. Wenn etwas gefunden wird, geben Sie ein aus ROLLBACK, um den DML-Vorgang abzubrechen, und dann ein RAISERRORSprichwort, dass Duplikate vorhanden sind.

Kollation

In meiner ersten Antwort (basierend auf dem ursprünglichen Wortlaut / den spärlichen Details der Frage und verfügbar im Revisionsverlauf hier ) hatte ich vorgeschlagen, möglicherweise eine binäre (dh _BIN2) Kollatierung zu verwenden. Nachdem wir nun wissen, was genau org_pathist, würde ich die Verwendung einer binären Sortierung nicht empfehlen. Da es diakritische Zeichen sein, Sie tun wollen Verwendung von sprachlichen Äquivalenzen machen.

Solomon Rutzky
quelle
Lassen Sie uns diese Diskussion im Chat fortsetzen .
Solomon Rutzky
0

Warum brauchst du eine PK?

Warum nicht einfach mit company_id als nicht gruppiertem Index arbeiten?

Sie sagten, die meisten Suchanfragen beziehen sich auf alle Einträge oder auf company_id.
Selten aktualisieren org_path selten
löschen
. Dies ist die einzige Tabelle, in der sie vorhanden ist

Die Antwort von Martin Smith
bringt Ihnen möglicherweise das, was Sie brauchen. Ich bin nicht vertraut damit, automatisch einen 4-Byte-Integer-Uniqueifier hinzuzufügen.
Vielleicht fehlt mir etwas, aber wenn Sie keine anderen Spalten indiziert haben, sehe ich in diesem Anwendungsfall keinen Sinn dafür

Wenn Sie sich Sorgen über DRI machen, sollten die Tabellen die Company-Tabelle als FK für company_id verwenden

Paparazzo
quelle
Hallo. In Bezug auf „ Warum nicht einfach gehen mit company_id als Nicht-Clustered - Index? “: Denn die 2 nach unten Seiten haben würde: 1) es wäre 1 weitere Sache seines Platz wegnimmt , während ein Clustered - Index ist der Tisch so ohne zusätzliches Element, und 2) Es würde immer noch eine RID-Suche erforderlich sein, um das NVARCHAR-Feld zu erhalten, es sei denn, dies wäre eine INCLUDESpalte, aber das ist noch schlimmer, da es lediglich die Tabelle dupliziert. Die PK ist zwar nicht erforderlich. Der wichtige Teil ist der Clustered Index. Aber sobald Sie die IDENTITÄT haben, können Sie genauso gut mit PK gehen. Und bitte sehen Sie den neuen Link in meiner Antwort für einen Rundgang durch Uniquifier 😃
Solomon Rutzky
@srutzky Aber es vermeidet einen 4-Byte-Integer-Uniqueifier, so dass ich das als Waschung sehe
Paparazzo
Bei weniger als 10.000 Zeilen spielt es keine Rolle. Sie müssen sich wahrscheinlich in Millionen von Zeilen befinden, bevor Sie den Effekt von nur 4 Bytes bemerken. Bei der Abfrage "Alle Zeilen abrufen" gibt es bei keiner dieser Optionen einen wirklichen Unterschied. Bei der Abfrage "get for company_id = @param" hilft es jedoch, wenn die Daten von company_id physisch geordnet werden, insbesondere wenn nicht für jede Zeile eine RID-Suche durchgeführt werden muss.
Solomon Rutzky
@ Srutzky Wash ist eine Wäsche - 10K oder 1G. Es ist nur etwas, das das OP berücksichtigen muss.
Paparazzo