Aggiungi relazione di chiave esterna tra due database


92

Ho due tabelle in due diversi database. In table1 (in database1) c'è una colonna chiamata column1 ed è una chiave primaria. Ora in table2 (in database2) c'è una colonna chiamata column2 e voglio aggiungerla come chiave esterna.

Ho provato ad aggiungerlo e mi ha dato il seguente errore:

Msg 1763, livello 16, stato 0, riga 1 I
riferimenti di chiave esterna tra database non sono supportati. Chiave esterna Database2.table2.

Messaggio 1750, livello 16, stato 0, riga 1
Impossibile creare il vincolo. Vedi errori precedenti.

Come posso farlo poiché le tabelle si trovano in database diversi.

Risposte:


84

È necessario gestire il vincolo referenziale tra i database utilizzando un trigger.


Fondamentalmente si crea un trigger di inserimento e aggiornamento per verificare l'esistenza della chiave nella tabella della chiave primaria. Se la chiave non esiste, ripristinare l'inserimento o l'aggiornamento e quindi gestire l'eccezione.

Esempio:

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

Modificato: solo per chiarire. Questo non è l'approccio migliore per imporre l'integrità referenziale. Idealmente vorresti entrambe le tabelle nello stesso db, ma se ciò non è possibile. Quindi quanto sopra è una potenziale soluzione per te.


4
@ John Hartsock: l'esempio precedente può facilmente fallire, senza aggiungere una gestione delle transazioni appropriata. Una discussione decente del tipo di problema che può verificarsi con "se non esiste () allora inserto" può essere trovato qui - stackoverflow.com/questions/108403/...
EBarr

17
@ John Hartsock - la tua soluzione ha una scappatoia: se uno dei due database viene ripristinato da un backup, i trigger non si attivano ovviamente. È così che possiamo ritrovarci con righe orfane.
AK

4
@AlexKuznetsov Esattamente. Come ho spiegato, questo non è l'approccio migliore ma un potenziale aggiramento.
John Hartsock

2
Questo è così sbagliato ... Spero solo che l'OP si renda conto che il solo fatto che sta chiedendo qualcosa del genere, è un sintomo che molto probabilmente sta facendo qualcosa di sbagliato ... per non parlare dei trigger ..
MeTitus

1
@Marco Come ho scritto nella mia risposta "Solo per chiarire. Questo non è l'approccio migliore per imporre l'integrità referenziale. Idealmente vorresti entrambe le tabelle nello stesso db ma se ciò non è possibile. Allora quanto sopra è un potenziale aggiramento tu." Ho spiegato che questa probabilmente non è una buona idea.
John Hartsock

48

Se è necessaria un'integrità solida come una roccia, avere entrambe le tabelle in un database e utilizzare un vincolo FK. Se la tua tabella genitore si trova in un altro database, nulla impedisce a chiunque di ripristinare quel database genitore da un vecchio backup e quindi hai degli orfani.

Questo è il motivo per cui FK tra database non è supportato.


27

Nella mia esperienza, il modo migliore per gestirlo quando la fonte autorevole primaria di informazioni per due tabelle correlate deve trovarsi in due database separati è sincronizzare una copia della tabella dalla posizione primaria alla posizione secondaria (usando T- SQL o SSIS con controllo degli errori appropriato: non è possibile troncare e ripopolare una tabella mentre ha un riferimento a una chiave esterna, quindi ci sono alcuni modi per skin il gatto sulla tabella che aggiorna).

Quindi aggiungi una relazione FK tradizionale nella seconda posizione alla tabella che è effettivamente una copia di sola lettura.

È possibile utilizzare un trigger o un processo pianificato nella posizione principale per mantenere aggiornata la copia.


1
Ri. "È possibile attivare o pianificare un processo nella posizione principale per mantenere la copia aggiornata": perché non utilizzare semplicemente la replica di SQL Server (in particolare il tipo Transazione vs. unione poiché la copia del Sottoscrittore (la copia che ha le tabelle che richiedono vincoli di chiave esterna) deve essere di sola lettura)? Vedi: link
Tom

@ Tom sì, puoi certamente usare la replica per mantenere una copia della tabella aggiornata in un database remoto.
Cade Roux

21

È possibile utilizzare il vincolo di controllo con una funzione definita dall'utente per eseguire il controllo. È più affidabile di un trigger. Può essere disabilitato e riabilitato quando necessario come le chiavi esterne e ricontrollato dopo un ripristino del database2.

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)

1
questa è una soluzione migliore della risposta accettata e puoi anche riutilizzarla su più tabelle
Milox

3

La risposta breve è che SQL Server (a partire da SQL 2008) non supporta le chiavi esterne del database incrociato, come afferma il messaggio di errore.

Sebbene non sia possibile avere integrità referenziale dichiarativa (FK), è possibile raggiungere lo stesso obiettivo utilizzando i trigger. È un po 'meno affidabile, perché la logica che scrivi potrebbe avere bug, ma ti porterà lì lo stesso.

Vedere la documentazione SQL @ http://msdn.microsoft.com/en-us/library/aa258254%28v=sql.80%29.aspx Quale stato:

I trigger vengono spesso utilizzati per applicare le regole aziendali e l'integrità dei dati. SQL Server fornisce l'integrità referenziale dichiarativa (DRI) tramite le istruzioni di creazione della tabella (ALTER TABLE e CREATE TABLE); tuttavia, DRI non fornisce integrità referenziale cross-database. Per applicare l'integrità referenziale (regole sulle relazioni tra le chiavi primarie ed esterne delle tabelle), utilizzare i vincoli di chiave primaria ed esterna (le parole chiave PRIMARY KEY e FOREIGN KEY di ALTER TABLE e CREATE TABLE). Se esistono vincoli nella tabella trigger, vengono verificati dopo l'esecuzione del trigger INSTEAD OF e prima dell'esecuzione del trigger AFTER. Se i vincoli vengono violati, le azioni del trigger INSTEAD OF vengono annullate e il trigger AFTER non viene eseguito (attivato).

C'è anche una discussione OK su SQLTeam - http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=31135


0

Come dice il messaggio di errore, questo non è supportato sul server sql. L'unico modo per garantire l'integrità refrerenziale è lavorare con i trigger.


1
Puoi spiegarmi con un esempio
Sam
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.