Ajouter une relation de clé étrangère entre deux bases de données

90

J'ai deux tables dans deux bases de données différentes. Dans table1 (dans database1), il y a une colonne appelée column1 et c'est une clé primaire. Maintenant, dans table2 (dans database2), il y a une colonne appelée column2 et je veux l'ajouter en tant que clé étrangère.

J'ai essayé de l'ajouter et cela m'a donné l'erreur suivante:

Msg 1763, niveau 16, état 0, ligne 1
Les références de clé étrangère entre bases de données ne sont pas prises en charge. Clé étrangère Database2.table2.

Msg 1750, niveau 16, état 0, ligne 1
Impossible de créer la contrainte. Voir les erreurs précédentes.

Comment puis-je faire cela puisque les tables sont dans des bases de données différentes.

Sam
la source

Réponses:

84

Vous devrez gérer la contrainte référentielle dans les bases de données à l'aide d'un déclencheur.


Fondamentalement, vous créez un déclencheur d'insertion, de mise à jour pour vérifier l'existence de la clé dans la table de clé primaire. Si la clé n'existe pas, annulez l'insertion ou la mise à jour, puis gérez l'exception.

Exemple:

Create Trigger dbo.MyTableTrigger ON dbo.MyTable, After Insert, Update
As
Begin

   If NOT Exists(select PK from OtherDB.dbo.TableName where PK in (Select FK from inserted) BEGIN
      -- Handle the Referential Error Here
   END

END

Édité: Juste pour clarifier. Ce n'est pas la meilleure approche pour faire respecter l'intégrité référentielle. Idéalement, vous voudriez que les deux tables soient dans la même base de données, mais si ce n'est pas possible. Ensuite, ce qui précède est un travail potentiel pour vous.

John Hartsock
la source
3
@John Hartsock - l'exemple ci-dessus peut facilement échouer, sans ajouter de gestion de transaction appropriée. Une discussion décente sur le type de problème qui peut survenir avec "sinon existe () alors insérer" peut être trouvée ici - stackoverflow.com/questions/108403
...
16
@John Hartsock - votre solution a une faille: si l'une des deux bases de données est restaurée à partir d'une sauvegarde, les déclencheurs ne se déclenchent bien sûr pas. C'est ainsi que nous pouvons nous retrouver avec des lignes orphelines.
AK
4
@AlexKuznetsov Exactement. Comme je l'ai expliqué, ce n'est pas la meilleure approche, mais une solution potentielle.
John Hartsock
2
C'est tellement faux ... J'espère juste que l'OP se rendra compte que le simple fait qu'il demande quelque chose comme ça, est un symptôme qu'il fait probablement quelque chose de mal ... et encore moins penser aux déclencheurs ..
MeTitus
1
@Marco Comme je l'ai publié dans ma réponse "Juste pour clarifier. Ce n'est pas la meilleure approche pour appliquer l'intégrité référentielle. Idéalement, vous voudriez que les deux tables soient dans la même base de données, mais si ce n'est pas possible. Alors ce qui précède est une solution potentielle pour tu." J'ai expliqué que ce n'est probablement pas une bonne idée.
John Hartsock
48

Si vous avez besoin d'une intégrité à toute épreuve, placez les deux tables dans une seule base de données et utilisez une contrainte FK. Si votre table parente se trouve dans une autre base de données, rien n'empêche quiconque de restaurer cette base de données parente à partir d'une ancienne sauvegarde, et vous avez alors des orphelins.

C'est pourquoi FK entre les bases de données n'est pas pris en charge.

AK
la source
27

D'après mon expérience, la meilleure façon de gérer cela lorsque la principale source d'informations faisant autorité pour deux tables liées doit se trouver dans deux bases de données distinctes est de synchroniser une copie de la table de l'emplacement principal vers l'emplacement secondaire (en utilisant T- SQL ou SSIS avec vérification des erreurs appropriée - vous ne pouvez pas tronquer et repeupler une table alors qu'elle a une référence de clé étrangère, il existe donc plusieurs façons de skinner le chat sur la mise à jour de la table).

Ajoutez ensuite une relation FK traditionnelle au deuxième emplacement de la table qui est en fait une copie en lecture seule.

Vous pouvez utiliser un déclencheur ou une tâche planifiée dans l'emplacement principal pour maintenir la copie à jour.

Cade Roux
la source
1
Ré. "Vous pouvez déclencher ou planifier un travail à l'emplacement principal pour maintenir la copie à jour": pourquoi ne pas simplement utiliser la réplication SQL Server (en particulier le type Transaction vs. Merge puisque la copie de l'abonné (la copie qui contient les tables nécessitant des contraintes de clé étrangère) doit être en lecture seule)? Voir: lien
Tom
@Tom oui, vous pouvez certainement utiliser la réplication pour conserver une copie de la table à jour dans une base de données distante.
Cade Roux
20

Vous pouvez utiliser la contrainte de vérification avec une fonction définie par l'utilisateur pour effectuer la vérification. C'est plus fiable qu'un déclencheur. Il peut être désactivé et réactivé si nécessaire comme les clés étrangères et revérifié après une restauration de base de données2.

CREATE FUNCTION dbo.fn_db2_schema2_tb_A
(@column1 INT) 
RETURNS BIT
AS
BEGIN
    DECLARE @exists bit = 0
    IF EXISTS (
      SELECT TOP 1 1 FROM DB2.SCHEMA2.tb_A 
      WHERE COLUMN_KEY_1 =  @COLUMN1
    ) BEGIN 
         SET @exists = 1 
      END;
      RETURN @exists
END
GO

ALTER TABLE db1.schema1.tb_S
  ADD CONSTRAINT CHK_S_key_col1_in_db2_schema2_tb_A
    CHECK(dbo.fn_db2_schema2_tb_A(key_col1) = 1)
Camilo J
la source
1
c'est une meilleure solution que la réponse acceptée et vous pouvez également la réutiliser sur plusieurs tables
Milox
3

La réponse courte est que SQL Server (à partir de SQL 2008) ne prend pas en charge les clés étrangères entre bases de données, comme l'indique le message d'erreur.

Bien que vous ne puissiez pas avoir d'intégrité référentielle déclarative (le FK), vous pouvez atteindre le même objectif en utilisant des déclencheurs. C'est un peu moins fiable, car la logique que vous écrivez peut avoir des bogues, mais elle vous y mènera tout de même.

Consultez la documentation SQL @ http://msdn.microsoft.com/en-us/library/aa258254%28v=sql.80%29.aspx Quel état:

Les déclencheurs sont souvent utilisés pour appliquer les règles métier et l'intégrité des données. SQL Server fournit l'intégrité référentielle déclarative (DRI) via les instructions de création de table (ALTER TABLE et CREATE TABLE); cependant, DRI ne fournit pas d'intégrité référentielle entre bases de données. Pour appliquer l'intégrité référentielle (règles sur les relations entre les clés primaire et étrangère des tables), utilisez des contraintes de clé primaire et étrangère (les mots clés PRIMARY KEY et FOREIGN KEY de ALTER TABLE et CREATE TABLE). Si des contraintes existent sur la table des déclencheurs, elles sont vérifiées après l'exécution du déclencheur INSTEAD OF et avant l'exécution du déclencheur AFTER. Si les contraintes sont violées, les actions du déclencheur INSTEAD OF sont annulées et le déclencheur AFTER n'est pas exécuté (déclenché).

Il y a aussi une discussion OK sur SQLTeam - http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=31135

EBarr
la source
0

Comme le message d'erreur l'indique, cela n'est pas pris en charge sur le serveur SQL. Le seul moyen d'assurer l'intégrité référentielle est de travailler avec des déclencheurs.

Jan
la source
1
Pouvez-vous m'expliquer avec un exemple
Sam