Verfügt SQL Server über eine Methode zur Auswahl zwischen einem eindeutigen Index und einem Primärschlüssel?
Zumindest ist es möglich, SqlServer anzuweisen, auf den Primärschlüssel zu verweisen, wenn ein Fremdschlüssel erstellt wird und alternative Schlüsselbeschränkungen oder eindeutige Indizes für die Tabelle vorhanden sind, auf die verwiesen wird.
Wenn auf den Primärschlüssel verwiesen werden muss, sollte in der Fremdschlüsseldefinition nur der Name der Tabelle angegeben werden, auf die verwiesen wird, und die Liste der Spalten, auf die verwiesen wird, sollte weggelassen werden:
ALTER TABLE Child
ADD CONSTRAINT FK_Child_Parent FOREIGN KEY (ParentID)
-- omit key columns of the referenced table
REFERENCES Parent /*(ParentID)*/;
Weitere Details unten.
Betrachten Sie das folgende Setup:
CREATE TABLE T (id int NOT NULL, a int, b int, c uniqueidentifier, filler binary(1000));
CREATE TABLE TRef (tid int NULL);
Dabei TRef
beabsichtigt die Tabelle, auf die Tabelle zu verweisen T
.
Um eine referenzielle Einschränkung zu erstellen, kann ein ALTER TABLE
Befehl mit zwei Alternativen verwendet werden:
ALTER TABLE TRef
ADD CONSTRAINT FK_TRef_T_1 FOREIGN KEY (tid) REFERENCES T (id);
ALTER TABLE TRef
ADD CONSTRAINT FK_TRef_T_2 FOREIGN KEY (tid) REFERENCES T;
Beachten Sie, dass im zweiten Fall keine Spalten der Tabelle angegeben werden, auf die verwiesen wird ( REFERENCES T
versus REFERENCES T (id)
).
Da noch keine Schlüsselindizes vorhanden sind T
, führt die Ausführung dieser Befehle zu Fehlern.
Der erste Befehl gibt folgenden Fehler zurück:
Meldung 1776, Ebene 16, Status 0, Zeile 4
Die referenzierte Tabelle 'T' enthält keine Primär- oder Kandidatenschlüssel , die mit der Liste der referenzierenden Spalten im Fremdschlüssel 'FK_TRef_T_1' übereinstimmen.
Der zweite Befehl gibt jedoch einen anderen Fehler zurück:
Nachricht 1773, Ebene 16, Status 0, Zeile 4
Der Fremdschlüssel 'FK_TRef_T_2' verweist implizit auf das Objekt 'T', auf dem kein Primärschlüssel definiert ist.
Sehen Sie, dass im ersten Fall die Erwartung Primär- oder Kandidatenschlüssel ist , während im zweiten Fall die Erwartung nur der Primärschlüssel ist .
Lassen Sie uns überprüfen, ob SqlServer mit dem zweiten Befehl etwas anderes als den Primärschlüssel verwendet oder nicht.
Wenn wir einige eindeutige Indizes und einen eindeutigen Schlüssel hinzufügen für T
:
CREATE UNIQUE INDEX IX_T_1 on T(id) INCLUDE (filler);
CREATE UNIQUE INDEX IX_T_2 on T(id) INCLUDE (c);
CREATE UNIQUE INDEX IX_T_3 ON T(id) INCLUDE (a, b);
ALTER TABLE T
ADD CONSTRAINT UQ_T UNIQUE CLUSTERED (id);
Befehl zum FK_TRef_T_1
Erstellen ist erfolgreich, aber Befehl zum FK_TRef_T_2
Erstellen schlägt mit Msg 1773 immer noch fehl.
Zum Schluss, wenn wir den Primärschlüssel hinzufügen T
:
ALTER TABLE T
ADD CONSTRAINT PK_T PRIMARY KEY NONCLUSTERED (id);
Befehl zur FK_TRef_T_2
Erstellung erfolgreich.
Lassen Sie uns überprüfen, auf welche Indizes der Tabelle T
von Fremdschlüsseln der Tabelle verwiesen wird TRef
:
select
ix.index_id,
ix.name as index_name,
ix.type_desc as index_type_desc,
fk.name as fk_name
from sys.indexes ix
left join sys.foreign_keys fk on
fk.referenced_object_id = ix.object_id
and fk.key_index_id = ix.index_id
and fk.parent_object_id = object_id('TRef')
where ix.object_id = object_id('T');
dies gibt zurück:
index_id index_name index_type_desc fk_name
--------- ----------- ----------------- ------------
1 UQ_T CLUSTERED NULL
2 IX_T_1 NONCLUSTERED FK_TRef_T_1
3 IX_T_2 NONCLUSTERED NULL
4 IX_T_3 NONCLUSTERED NULL
5 PK_T NONCLUSTERED FK_TRef_T_2
siehe das FK_TRef_T_2
entspricht PK_T
.
Also, ja, bei Verwendung der REFERENCES T
Syntax wird der Fremdschlüssel von TRef
dem Primärschlüssel von zugeordnet T
.
Ich konnte ein solches Verhalten, das in der SqlServer-Dokumentation beschrieben ist, nicht direkt finden, aber die dedizierte Meldung 1773 legt nahe, dass es nicht zufällig ist. Wahrscheinlich bietet eine solche Implementierung die Konformität mit dem SQL-Standard. Nachfolgend finden Sie einen kurzen Auszug aus Abschnitt 11.8 von ANSI / ISO 9075-2: 2003
11 Schemadefinition und -manipulation
11.8 <Definition der referenziellen Einschränkung>
Funktion
Geben Sie eine referenzielle Einschränkung an.
Format
<referential constraint definition> ::=
FOREIGN KEY <left paren> <referencing columns> <right paren>
<references specification>
<references specification> ::=
REFERENCES <referenced table and columns>
[ MATCH <match type> ]
[ <referential triggered action> ]
...
Syntaxregeln
...
3) Fall:
...
b) Wenn in der <referenzierten Tabelle und Spalten> keine <Referenzspaltenliste> angegeben ist, muss der Tabellendeskriptor der referenzierten Tabelle eine eindeutige Einschränkung enthalten, die PRIMARY KEY angibt. Sei referenzierte Spalten die Spalte oder Spalten, die durch die eindeutigen Spalten in dieser eindeutigen Einschränkung identifiziert werden, und sei referenzierte Spalte
eine solche Spalte. Es wird davon ausgegangen, dass die <referenzierte Tabelle und Spalten> implizit eine <Referenzspaltenliste> angeben, die mit dieser <eindeutigen Spaltenliste> identisch ist.
...
Transact-SQL unterstützt und erweitert ANSI SQL. Es entspricht jedoch nicht genau dem SQL-Standard. Es gibt ein Dokument mit dem Namen SQL Server Transact-SQL ISO / IEC 9075-2-Standardunterstützungsdokument (kurz MS-TSQLISO02, siehe hier ), das den Unterstützungsgrad beschreibt, der von Transact-SQL bereitgestellt wird. Das Dokument listet Erweiterungen und Variationen des Standards auf. Beispielsweise wird dokumentiert, dass die MATCH
Klausel in der Definition der referenziellen Einschränkung nicht unterstützt wird. Es gibt jedoch keine dokumentierten Abweichungen, die für das zitierte Stück Standard relevant sind. Meiner Meinung nach ist das beobachtete Verhalten ausreichend dokumentiert.
Und unter Verwendung der REFERENCES T (<reference column list>)
Syntax scheint es, dass SqlServer den ersten geeigneten nicht gruppierten Index unter den Indizes der Tabelle auswählt, auf die verwiesen wird (der mit der geringsten index_id
scheinbaren, nicht der mit der kleinsten physischen Größe, wie in den Fragenkommentaren angenommen), oder den gruppierten Index, falls dies der Fall ist Anzüge und es gibt keine geeigneten nicht gruppierten Indizes. Ein solches Verhalten scheint seit SqlServer 2008 (Version 10.0) konsistent zu sein. Dies ist natürlich nur eine Beobachtung, in diesem Fall keine Garantie.