Я работаю с устаревшей базой данных, которая была импортирована из MS Access. Существует около двадцати таблиц с некластеризованными уникальными первичными ключами, которые были созданы во время обновления MS Access> SQL Server.
Многие из этих таблиц также имеют уникальные некластеризованные индексы, которые являются дубликатами первичного ключа.
Я пытаюсь это убрать.
Но я обнаружил, что после того, как я воссоздаю первичные ключи как кластерные индексы, а затем пытаюсь перестроить внешний ключ, внешний ключ ссылается на старый, дублирующий индекс (который был уникальным).
Я знаю это, потому что это не позволит мне удалить дубликаты индексов.
Я думаю, что SQL Server всегда будет выбирать первичный ключ, если он существует. Есть ли в SQL Server способ выбора между уникальным индексом и первичным ключом?
Чтобы дублировать проблему (в 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
Сообщение об ошибке:
Сообщение 3723, уровень 16, состояние 6, строка 36 Явный индекс DROP INDEX недопустим для индекса 'Parent.IX_Parent'. Он используется для применения ограничений FOREIGN KEY.
Ответы:
(Отсутствие) документации говорит о том, что это поведение - деталь реализации, поэтому оно не определено и может быть изменено в любое время.
Это резко контрастирует с CREATE FULLTEXT INDEX , где вы должны указать имя индекса для присоединения - AFAIK, нет недокументированного
FOREIGN KEY
синтаксиса, чтобы сделать эквивалент (хотя теоретически это может произойти в будущем).Как уже упоминалось, имеет смысл, что SQL Server выбирает наименьший физический индекс, чтобы связать внешний ключ. Если вы измените сценарий для создания уникального ограничения как
CLUSTERED
, сценарий «работает» в 2008 R2. Но это поведение все еще не определено и на него нельзя полагаться.Как и в случае с большинством устаревших приложений, вам нужно просто разобраться с мелочами и привести в порядок вещи.
источник
По крайней мере, можно указать SqlServer для ссылки на первичный ключ, когда создается внешний ключ и существуют альтернативные ограничения ключа или уникальные индексы в таблице, на которую ссылаются.
Если на первичный ключ необходимо ссылаться, то в определении внешнего ключа должно быть указано только имя таблицы, на которую ссылаются, а список столбцов, на которые делается ссылка:
Более подробная информация ниже.
Рассмотрим следующую настройку:
где таблица
TRef
предназначена для ссылки на таблицуT
.Для создания ссылочного ограничения можно использовать
ALTER TABLE
команду с двумя альтернативами:обратите внимание, что во втором случае столбцы таблицы, на которую делается ссылка, не указываются (по
REFERENCES T
сравнению сREFERENCES T (id)
).Поскольку пока нет ключевых индексов
T
, выполнение этих команд приведет к ошибкам.Первая команда возвращает следующую ошибку:
Вторая команда, однако, возвращает другую ошибку:
обратите внимание, что в первом случае ожидание является первичным ключом или ключом-кандидатом , тогда как во втором случае ожидание является только первичным ключом .
Давайте проверим, будет ли SqlServer использовать что-то кроме первичного ключа со второй командой или нет.
Если мы добавим несколько уникальных индексов и уникальный ключ на
T
:команда для
FK_TRef_T_1
создания успешно, но команда дляFK_TRef_T_2
создания все еще терпит неудачу с Msg 1773.Наконец, если мы добавим первичный ключ на
T
:Команда для
FK_TRef_T_2
создания успешна.Давайте проверим, на какие индексы таблицы
T
ссылаются внешние ключи таблицыTRef
:это возвращает:
увидеть, что
FK_TRef_T_2
соответствуетPK_T
.Итак, да, с использованием
REFERENCES T
синтаксиса внешний ключTRef
сопоставляется первичному ключуT
.Я не смог найти такое поведение, описанное в документации SqlServer напрямую, но выделенная Msg 1773 предполагает, что это не случайно. Вероятно, такая реализация обеспечивает соответствие стандарту SQL, ниже приведен небольшой отрывок из раздела 11.8 стандарта ANSI / ISO 9075-2: 2003.
Transact-SQL поддерживает и расширяет ANSI SQL. Однако это не совсем соответствует стандарту SQL. Существует документ под названием SQL Server Transact-SQL, соответствующий стандарту поддержки стандартов ISO / IEC 9075-2 (кратко MS-TSQLISO02, см. Здесь ), в котором описывается уровень поддержки, предоставляемый Transact-SQL. В документе перечислены расширения и варианты стандарта. Например, он документирует, что
MATCH
предложение не поддерживается в определении ссылочного ограничения. Но нет никаких задокументированных изменений, относящихся к цитируемому стандарту. Итак, мое мнение таково, что наблюдаемое поведение достаточно задокументировано.И с использованием
REFERENCES T (<reference column list>)
синтаксиса кажется, что SqlServer выбирает первый подходящий некластеризованный индекс среди индексов таблицы, на которую ссылаются (с наименьшим внешним видомindex_id
, а не с наименьшим физическим размером, как предполагается в комментариях к вопросу), или кластеризованный индекс, если он подходит и нет подходящих некластеризованных индексов. Такое поведение кажется последовательным со времен SqlServer 2008 (версия 10.0). Это просто наблюдение, конечно, никаких гарантий в этом случае.источник