Ich arbeite mit einer Legacy-Datenbank, die aus MS Access importiert wurde. Es gibt ungefähr zwanzig Tabellen mit nicht gruppierten, eindeutigen Primärschlüsseln, die während des MS Access> SQL Server-Upgrades erstellt wurden.
Viele dieser Tabellen haben auch eindeutige, nicht gruppierte Indizes, die Duplikate des Primärschlüssels sind.
Ich versuche das aufzuräumen.
Nachdem ich die Primärschlüssel als Clustered-Indizes neu erstellt und dann versucht habe, den Fremdschlüssel neu zu erstellen, verweist der Fremdschlüssel auf den alten, doppelten Index (der eindeutig war).
Ich weiß das, weil ich dadurch die doppelten Indizes nicht löschen kann.
Ich würde denken, SQL Server würde immer einen Primärschlüssel wählen, wenn einer existiert. Verfügt SQL Server über eine Methode zur Auswahl zwischen einem eindeutigen Index und einem Primärschlüssel?
So duplizieren Sie das Problem (unter SQL Server 2008 R2):
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Child') DROP TABLE Child
GO
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Parent') DROP TABLE Parent
GO
-- Create the parent table
CREATE TABLE Parent (ParentID INT NOT NULL IDENTITY(1,1))
-- Make the parent table a heap
ALTER TABLE Parent ADD CONSTRAINT PK_Parent PRIMARY KEY NONCLUSTERED (ParentID)
-- Create the duplicate index on the parent table
CREATE UNIQUE NONCLUSTERED INDEX IX_Parent ON Parent (ParentID)
-- Create the child table
CREATE TABLE Child (ChildID INT NOT NULL IDENTITY(1,1), ParentID INT NOT NULL )
-- Give the child table a normal PKey
ALTER TABLE Child ADD CONSTRAINT PK_Child PRIMARY KEY CLUSTERED (ChildID)
-- Create a foreign key relationship with the Parent table on ParentID
ALTER TABLE Child ADD CONSTRAINT FK_Child FOREIGN KEY (ParentID)
REFERENCES Parent (ParentID) ON DELETE CASCADE NOT FOR REPLICATION
-- Try to clean this up
-- Drop the foreign key constraint on the Child table
ALTER TABLE Child DROP CONSTRAINT FK_Child
-- Drop the primary key constraint on the Parent table
ALTER TABLE Parent DROP CONSTRAINT PK_Parent
-- Recreate the primary key on Parent as a clustered index
ALTER TABLE Parent ADD CONSTRAINT PK_Parent PRIMARY KEY CLUSTERED (ParentID)
-- Recreate the foreign key in Child pointing to parent ID
ALTER TABLE Child ADD CONSTRAINT FK_Child FOREIGN KEY (ParentID)
REFERENCES Parent (ParentID) ON DELETE CASCADE NOT FOR REPLICATION
-- Try to drop the duplicate index on Parent
DROP INDEX IX_Parent ON Parent
Fehlermeldung:
Meldung 3723, Ebene 16, Status 6, Zeile 36 Ein expliziter DROP INDEX ist für den Index 'Parent.IX_Parent' nicht zulässig. Es wird für die Durchsetzung von FOREIGN KEY-Einschränkungen verwendet.
Antworten:
Die (fehlende) Dokumentation legt nahe, dass dieses Verhalten ein Implementierungsdetail ist und daher undefiniert ist und jederzeit geändert werden kann.
Dies steht in krassem Gegensatz zu CREATE FULLTEXT INDEX , wo Sie den Namen eines Index angeben müssen, an den angehängt werden soll - AFAIK, es gibt keine undokumentierte
FOREIGN KEY
Syntax, um das Äquivalent zu tun (obwohl dies theoretisch in der Zukunft möglich sein könnte).Wie bereits erwähnt, ist es sinnvoll, dass SQL Server den kleinsten physischen Index auswählt, dem der Fremdschlüssel zugeordnet werden soll. Wenn Sie das Skript ändern, um die eindeutige Einschränkung als zu erstellen
CLUSTERED
, "funktioniert" das Skript unter 2008 R2. Dieses Verhalten ist jedoch noch nicht definiert und sollte nicht als verlässlich angesehen werden.Wie bei den meisten Legacy-Anwendungen müssen Sie sich nur auf das Wesentliche konzentrieren und die Dinge bereinigen.
quelle
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:
Weitere Details unten.
Betrachten Sie das folgende Setup:
Dabei
TRef
beabsichtigt die Tabelle, auf die Tabelle zu verweisenT
.Um eine referenzielle Einschränkung zu erstellen, kann ein
ALTER TABLE
Befehl mit zwei Alternativen verwendet werden:Beachten Sie, dass im zweiten Fall keine Spalten der Tabelle angegeben werden, auf die verwiesen wird (
REFERENCES T
versusREFERENCES 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:
Der zweite Befehl gibt jedoch einen anderen Fehler zurück:
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
:Befehl zum
FK_TRef_T_1
Erstellen ist erfolgreich, aber Befehl zumFK_TRef_T_2
Erstellen schlägt mit Msg 1773 immer noch fehl.Zum Schluss, wenn wir den Primärschlüssel hinzufügen
T
: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 wirdTRef
:dies gibt zurück:
siehe das
FK_TRef_T_2
entsprichtPK_T
.Also, ja, bei Verwendung der
REFERENCES T
Syntax wird der Fremdschlüssel vonTRef
dem Primärschlüssel von zugeordnetT
.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
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 geringstenindex_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.quelle