Je travaille avec une base de données héritée qui a été importée de MS Access. Il existe une vingtaine de tables avec des clés primaires uniques non groupées qui ont été créées lors de la mise à niveau de MS Access> SQL Server.
Un grand nombre de ces tables ont également des index uniques non clusterisés qui sont des doublons de la clé primaire.
J'essaie de nettoyer ça.
Mais ce que j'ai trouvé, c'est après avoir recréé les clés primaires sous forme d'index cluster, puis essayer de reconstruire la clé étrangère, la clé étrangère fait référence à l'ancien index en double (qui était unique).
Je le sais car cela ne me permettra pas de supprimer les index en double.
Je pense que SQL Server choisirait toujours une clé primaire si elle existait. SQL Server a-t-il une méthode pour choisir entre un index unique et une clé primaire?
Pour dupliquer le problème (sur 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
Message d'erreur:
Msg 3723, niveau 16, état 6, ligne 36 Un DROP INDEX explicite n'est pas autorisé sur l'index 'Parent.IX_Parent'. Il est utilisé pour l'application des contraintes FOREIGN KEY.
Réponses:
Le (manque de) documentation suggère que ce comportement est un détail d'implémentation et n'est donc pas défini et sujet à changement à tout moment.
C'est en contraste frappant avec CREATE FULLTEXT INDEX , où vous devez spécifier le nom d'un index à attacher - AFAIK, il n'y a pas de
FOREIGN KEY
syntaxe non documentée pour faire l'équivalent (bien qu'en théorie, il pourrait y en avoir dans le futur).Comme mentionné, il est logique que SQL Server choisisse le plus petit index physique auquel associer la clé étrangère. Si vous modifiez le script pour créer la contrainte unique en tant que
CLUSTERED
, le script "fonctionne" sur 2008 R2. Mais ce comportement n'est pas encore défini et ne doit pas être invoqué.Comme pour la plupart des applications héritées, il vous suffira de passer aux choses sérieuses et de nettoyer les choses.
la source
Au moins, il est possible de diriger SqlServer pour référencer la clé primaire, lorsque la clé étrangère est créée et que des contraintes de clé alternatives ou des index uniques existent sur la table référencée.
Si la clé primaire doit être référencée, seul le nom de la table référencée doit être spécifié dans la définition de la clé étrangère et la liste des colonnes référencées doit être omise:
Plus de détails ci-dessous.
Considérez la configuration suivante:
où table
TRef
entend référencer la tableT
.Pour créer une contrainte référentielle, on peut utiliser la
ALTER TABLE
commande avec deux alternatives:notez que dans le second cas, aucune colonne de la table référencée n'est spécifiée (
REFERENCES T
par rapport àREFERENCES T (id)
).Puisqu'il n'y a pas
T
encore d' index clés sur , l'exécution de ces commandes générera des erreurs.La première commande renvoie l'erreur suivante:
La deuxième commande, cependant, renvoie une erreur différente:
voir que dans le premier cas, l'attente est la clé primaire ou candidate , tandis que dans le second cas, l'attente est la clé primaire uniquement.
Vérifions si SqlServer utilisera autre chose que la clé primaire avec la deuxième commande ou non.
Si nous ajoutons des index uniques et une clé unique sur
T
:commande de
FK_TRef_T_1
création réussit, mais la commande deFK_TRef_T_2
création échoue toujours avec Msg 1773.Enfin, si nous ajoutons la clé primaire sur
T
:commande de
FK_TRef_T_2
création réussit.Vérifions quels index de la table
T
sont référencés par les clés étrangères de la tableTRef
:cela renvoie:
voir qui
FK_TRef_T_2
correspondent àPK_T
.Donc, oui, avec l'utilisation de la
REFERENCES T
syntaxe, la clé étrangère deTRef
est mappée à la clé primaire deT
.Je n'ai pas pu trouver un tel comportement décrit directement dans la documentation SqlServer, mais le Msg dédié 1773 suggère que ce n'est pas accidentel. Une telle implémentation est probablement conforme à la norme SQL, voici un court extrait de la section 11.8 de la norme ANSI / ISO 9075-2: 2003
Transact-SQL prend en charge et étend ANSI SQL. Cependant, il n'est pas exactement conforme à la norme SQL. Il existe un document nommé SQL Server Transact-SQL ISO / IEC 9075-2 Standards Support Document (MS-TSQLISO02 en bref, voir ici ) décrivant le niveau de prise en charge fourni par Transact-SQL. Le document répertorie les extensions et les variations de la norme. Par exemple, il documente que la
MATCH
clause n'est pas prise en charge dans la définition de contrainte référentielle. Mais il n'y a pas de variations documentées pertinentes pour l'élément de norme cité. Donc, mon opinion est que le comportement observé est suffisamment documenté.Et avec l'utilisation de la
REFERENCES T (<reference column list>)
syntaxe, il semble que SqlServer sélectionne le premier index non cluster approprié parmi les index de la table référencée (celui avec le moinsindex_id
apparemment, pas celui avec la plus petite taille physique comme supposé dans les commentaires de la question), ou un index cluster si c'est le cas convient et il n'y a pas d'index non cluster adaptés. Un tel comportement semble cohérent depuis SqlServer 2008 (version 10.0). Ceci est juste une observation bien sûr, aucune garantie dans ce cas.la source