Pracuję ze starszą bazą danych, która została zaimportowana z MS Access. Istnieje około dwudziestu tabel z nieklastrowanymi, unikatowymi kluczami głównymi, które zostały utworzone podczas aktualizacji MS Access> SQL Server.
Wiele z tych tabel ma także unikalne, nieklastrowane indeksy, które są duplikatami klucza podstawowego.
Próbuję to oczyścić.
Ale to, co znalazłem, to po tym, jak odtworzyłem klucze podstawowe jako indeksy klastrowe, a następnie próbuję odbudować klucz obcy, klucz obcy odnosi się do starego, zduplikowanego indeksu (który był unikalny).
Wiem o tym, ponieważ nie pozwoli mi upuścić zduplikowanych indeksów.
Myślę, że SQL Server zawsze wybiera klucz podstawowy, jeśli taki istnieje. Czy SQL Server ma metodę wyboru między unikalnym indeksem a kluczem podstawowym?
Aby zduplikować problem (w 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
Błąd msg:
Msg 3723, poziom 16, stan 6, wiersz 36 Wyraźny INDEKS DROP nie jest dozwolony w indeksie „Parent.IX_Parent”. Jest używany do wymuszania ograniczeń na klucz OBCY.
Odpowiedzi:
(Brak) dokumentacji sugeruje, że takie zachowanie jest szczegółem implementacji, a zatem jest niezdefiniowane i może ulec zmianie w dowolnym momencie.
Stanowi to wyraźny kontrast w stosunku do TWORZENIA PEŁNOTEKSTOWEGO INDEKSU , w którym musisz podać nazwę indeksu, do którego chcesz dołączyć - AFAIK, nie ma nieudokumentowanej
FOREIGN KEY
składni, aby wykonać równoważny (choć teoretycznie może być w przyszłości).Jak wspomniano, ma sens, że SQL Server wybiera najmniejszy indeks fizyczny, z którym ma zostać skojarzony klucz obcy. Jeśli zmienisz skrypt, aby utworzyć unikalne ograniczenie as
CLUSTERED
, skrypt „zadziała” na 2008 R2. Ale to zachowanie jest nadal nieokreślone i nie należy na nim polegać.Podobnie jak w przypadku większości starszych aplikacji, musisz po prostu przejść do drobiazgowości i posprzątać.
źródło
Przynajmniej możliwe jest skierowanie SqlServer do referencyjnego klucza podstawowego, gdy tworzony jest klucz obcy i istnieją alternatywne ograniczenia klucza lub unikalne indeksy w tabeli, do której się odwołuje.
Jeśli należy odwoływać się do klucza podstawowego, w definicji klucza obcego należy podać tylko nazwę tabeli, do której następuje odwołanie, a listę odwoływanych kolumn należy pominąć:
Więcej szczegółów poniżej.
Rozważ następującą konfigurację:
gdzie tabela
TRef
zamierza odwoływać się do tabeliT
.Aby utworzyć ograniczenie referencyjne, można użyć
ALTER TABLE
polecenia z dwiema alternatywami:zauważ, że w drugim przypadku nie podano żadnych kolumn tabeli, do której następuje odwołanie (w
REFERENCES T
porównaniuREFERENCES T (id)
).Ponieważ nie ma jeszcze żadnych indeksów kluczy
T
, wykonanie tych poleceń spowoduje wygenerowanie błędów.Pierwsze polecenie zwraca następujący błąd:
Drugie polecenie zwraca jednak inny błąd:
zobacz, że w pierwszym przypadku oczekiwanie jest kluczem podstawowym lub kluczem kandydującym , podczas gdy w drugim przypadku oczekiwanie jest tylko kluczem podstawowym .
Sprawdźmy, czy SqlServer użyje czegoś innego niż klucz podstawowy przy drugim poleceniu, czy nie.
Jeśli dodamy kilka unikalnych indeksów i unikalny klucz do
T
:polecenie
FK_TRef_T_1
tworzenia powiodło się, ale polecenieFK_TRef_T_2
utworzenia nadal nie działa z Msg 1773.Wreszcie, jeśli dodamy klucz podstawowy do
T
:polecenie
FK_TRef_T_2
tworzenia powiodło się.Sprawdźmy, do których indeksów tabeli
T
odwołują się obce klucze tabeliTRef
:to zwraca:
zobacz, że
FK_TRef_T_2
odpowiadaPK_T
.Tak, przy użyciu
REFERENCES T
składni klucz obcyTRef
jest odwzorowywany na klucz podstawowy zT
.Nie byłem w stanie znaleźć takiego zachowania opisanego bezpośrednio w dokumentacji SqlServer, ale dedykowany Msg 1773 sugeruje, że nie jest to przypadek. Prawdopodobnie taka implementacja zapewnia zgodność ze standardem SQL, poniżej znajduje się krótki fragment z sekcji 11.8 ANSI / ISO 9075-2: 2003
Transact-SQL obsługuje i rozszerza ANSI SQL. Nie jest jednak dokładnie zgodny ze standardem SQL. Istnieje dokument o nazwie SQL Server Transact-SQL Standard ISO / IEC 9075-2 Dokument wsparcia standardów (w skrócie MS-TSQLISO02, patrz tutaj ) opisujący poziom wsparcia zapewnianego przez Transact-SQL. Dokument zawiera listę rozszerzeń i odmian standardu. Na przykład dokumentuje, że
MATCH
klauzula nie jest obsługiwana w definicji ograniczenia referencyjnego. Ale nie ma udokumentowanych zmian odnoszących się do cytowanego standardu. Moim zdaniem obserwowane zachowanie jest wystarczająco udokumentowane.I przy użyciu
REFERENCES T (<reference column list>)
składni wydaje się, że SqlServer wybiera pierwszy odpowiedni indeks nieklastrowany spośród indeksów tabeli, do której się odwołuje (ten, któryindex_id
wydaje się najmniej widoczny, a nie ten o najmniejszym rozmiarze fizycznym, jak zakładano w komentarzach do pytania), lub indeks klastrowany, jeśli pasuje i nie ma odpowiednich indeksów nieklastrowanych. Takie zachowanie wydaje się być spójne od SqlServer 2008 (wersja 10.0). To oczywiście tylko obserwacja, w tym przypadku nie ma gwarancji.źródło