Estou trabalhando com um banco de dados herdado que foi importado do MS Access. Existem cerca de vinte tabelas com chaves primárias exclusivas e sem cluster, que foram criadas durante a atualização do MS Access> SQL Server.
Muitas dessas tabelas também possuem índices exclusivos, sem cluster, duplicados da chave primária.
Estou tentando limpar isso.
Mas o que descobri é que depois de recriar as chaves primárias como índices agrupados e, em seguida, tentar reconstruir a chave estrangeira, a chave estrangeira faz referência ao antigo índice duplicado (que era único).
Eu sei disso porque não vai me deixar largar os índices duplicados.
Eu acho que o SQL Server sempre escolheria uma chave primária, se existir. O SQL Server tem um método de escolher entre um índice exclusivo e uma chave primária?
Para duplicar o problema (no 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
Mensagem de erro:
Msg 3723, Nível 16, Estado 6, Linha 36 Um DROP INDEX explícito não é permitido no índice 'Parent.IX_Parent'. Ele está sendo usado para imposição de restrição FOREIGN KEY.
Respostas:
A (falta de) documentação sugere que esse comportamento é um detalhe de implementação e, portanto, é indefinido e sujeito a alterações a qualquer momento.
Isso contrasta fortemente com CREATE FULLTEXT INDEX , no qual é necessário especificar o nome de um índice ao qual anexar - AFAIK, não há
FOREIGN KEY
sintaxe não documentada para fazer o equivalente (embora teoricamente possa haver no futuro).Como mencionado, faz sentido que o SQL Server escolha o menor índice físico ao qual associar a chave estrangeira. Se você alterar o script para criar a restrição exclusiva como
CLUSTERED
, o script "funcionará" no 2008 R2. Mas esse comportamento ainda é indefinido e não deve ser invocado.Tal como acontece com a maioria dos aplicativos herdados, você só precisa fazer as coisas necessárias e limpar.
fonte
Pelo menos, é possível direcionar o SqlServer para referenciar a chave primária, quando uma chave estrangeira estiver sendo criada e restrições de chave alternativas ou índices exclusivos existirem na tabela que está sendo referenciada.
Se a chave primária precisar ser referenciada, apenas o nome da tabela que está sendo referenciada deve ser especificado na definição de chave estrangeira e a lista de colunas que estão sendo referenciadas deve ser omitida:
Mais detalhes abaixo.
Considere a seguinte configuração:
onde tabela
TRef
pretende fazer referência a tabelaT
.Para criar uma restrição referencial, pode-se usar o
ALTER TABLE
comando com duas alternativas:observe que no segundo caso, nenhuma coluna da tabela que está sendo referenciada é especificada (
REFERENCES T
versusREFERENCES T (id)
).Como ainda não há índices de chave
T
, a execução desses comandos gerará erros.O primeiro comando retorna o seguinte erro:
O segundo comando, no entanto, retorna um erro diferente:
veja que, no primeiro caso, a expectativa é chave primária ou candidata , enquanto que no segundo caso, a expectativa é apenas chave primária .
Vamos verificar se o SqlServer usará algo diferente da chave primária com o segundo comando ou não.
Se adicionarmos alguns índices exclusivos e chave exclusiva em
T
:comando para
FK_TRef_T_1
criação é bem-sucedido, mas o comando paraFK_TRef_T_2
criação ainda falha com a mensagem 1773.Por fim, se adicionarmos a chave primária em
T
:comando para
FK_TRef_T_2
criação bem-sucedido.Vamos verificar quais índices da tabela
T
são referenciados por chaves estrangeiras da tabelaTRef
:isso retorna:
veja que
FK_TRef_T_2
corresponde aPK_T
.Portanto, sim, com o uso da
REFERENCES T
sintaxe, a chave estrangeira deTRef
é mapeada para a chave primária deT
.Não consegui encontrar esse comportamento descrito diretamente na documentação do SqlServer, mas a Msg 1773 dedicada sugere que não é acidental. Provavelmente, essa implementação fornece conformidade com o Padrão SQL, abaixo está um pequeno trecho da seção 11.8 da ANSI / ISO 9075-2: 2003
O Transact-SQL suporta e estende o ANSI SQL. No entanto, não está em conformidade com o SQL Standard exatamente. Existe um documento chamado Documento de Suporte de Padrões do SQL Server Transact-SQL ISO / IEC 9075-2 (MS-TSQLISO02, resumindo, veja aqui ) descrevendo o nível de suporte fornecido pelo Transact-SQL. O documento lista extensões e variações para o padrão. Por exemplo, documenta que a
MATCH
cláusula não é suportada na definição de restrição referencial. Mas não há variações documentadas relevantes para a peça citada do padrão. Então, minha opinião é que o comportamento observado é documentado o suficiente.E, com o uso da
REFERENCES T (<reference column list>)
sintaxe, parece que o SqlServer seleciona o primeiro índice não clusterizado adequado entre os índices da tabela que está sendo referenciada (aquele com o menosindex_id
aparentemente, não aquele com o menor tamanho físico conforme assumido nos comentários da pergunta) ou o índice agrupado, se processos e não há índices não clusterizados adequados. Esse comportamento parece ser consistente desde o SqlServer 2008 (versão 10.0). Esta é apenas uma observação, é claro, não há garantias neste caso.fonte