Dodaj relację klucza obcego między dwiema bazami danych

90

Mam dwie tabele w dwóch różnych bazach danych. W tabeli1 (baza danych1) znajduje się kolumna o nazwie kolumna1 i jest to klucz podstawowy. Teraz w table2 (w database2) jest kolumna o nazwie column2 i chcę ją dodać jako klucz obcy.

Próbowałem go dodać i dał mi następujący błąd:

Msg 1763, poziom 16, stan 0, wiersz 1
Odwołania do kluczy obcych między bazami danych nie są obsługiwane. Klucz obcy Database2.table2.

Msg 1750, poziom 16, stan 0, wiersz 1
Nie można utworzyć ograniczenia. Zobacz poprzednie błędy.

Jak to zrobić, skoro tabele znajdują się w różnych bazach danych.

Sam
źródło

Odpowiedzi:

84

Będziesz musiał zarządzać ograniczeniem referencyjnym w bazach danych przy użyciu wyzwalacza.


Zasadniczo tworzysz wstawianie, aktualizowanie wyzwalacza, aby zweryfikować istnienie klucza w tabeli kluczy podstawowych. Jeśli klucz nie istnieje, przywróć operację wstawiania lub aktualizacji, a następnie obsłuż wyjątek.

Przykład:

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

Edytowano: tylko dla wyjaśnienia. To nie jest najlepsze podejście do egzekwowania integralności referencyjnej. Idealnie byłoby, gdyby obie tabele znajdowały się w tej samej bazie danych, ale jeśli nie jest to możliwe. W takim razie powyższe jest potencjalnym rozwiązaniem dla Ciebie.

John Hartsock
źródło
3
@John Hartsock - powyższy przykład może łatwo zawieść, bez dodania odpowiedniej obsługi transakcji. Przyzwoitą dyskusję na temat rodzaju problemu, który może wystąpić w przypadku polecenia „jeśli nie istnieje (), to wstaw” można znaleźć tutaj - stackoverflow.com/questions/108403/…
EBarr
16
@John Hartsock - Twoje rozwiązanie ma lukę: jeśli jedna z dwóch baz danych zostanie przywrócona z kopii zapasowej, wyzwalacze oczywiście nie zostaną uruchomione. W ten sposób możemy skończyć z osieroconymi rzędami.
AK
4
@AlexKuznetsov Dokładnie. Jak wyjaśniłem, nie jest to najlepsze podejście, ale potencjalne obejście.
John Hartsock,
2
To jest po prostu takie złe ... Mam tylko nadzieję, że OP zdaje sobie sprawę, że sam fakt, że prosi o coś takiego, jest symptomem tego, że najprawdopodobniej robi coś złego ... nie mówiąc już o wyzwalaczach ..
MeTitus
1
@Marco Tak jak zamieściłem w mojej odpowiedzi „Tylko dla wyjaśnienia. To nie jest najlepsze podejście do wymuszania integralności referencyjnej. Idealnie byłoby, gdyby obie tabele znajdowały się w tej samej bazie danych, ale jeśli nie jest to możliwe. W takim przypadku powyższe można obejść ty." Wyjaśniłem, że to chyba nie jest dobry pomysł.
John Hartsock
48

Jeśli potrzebujesz solidnej integralności, miej obie tabele w jednej bazie danych i użyj ograniczenia FK. Jeśli twoja tabela nadrzędna znajduje się w innej bazie danych, nic nie stoi na przeszkodzie, aby ktokolwiek mógł przywrócić tę nadrzędną bazę danych ze starej kopii zapasowej, a wtedy masz sieroty.

Z tego powodu FK między bazami danych nie jest obsługiwany.

AK
źródło
27

Z mojego doświadczenia wynika, że ​​najlepszym sposobem rozwiązania tego problemu, gdy główne autorytatywne źródło informacji dla dwóch powiązanych tabel musi znajdować się w dwóch oddzielnych bazach danych, jest zsynchronizowanie kopii tabeli z lokalizacji podstawowej do lokalizacji dodatkowej (przy użyciu T- SQL lub SSIS z odpowiednim sprawdzaniem błędów - nie można obcinać i ponownie zapełniać tabeli, gdy ma ona odniesienie do klucza obcego, więc istnieje kilka sposobów na skórowanie kota przy aktualizacji tabeli).

Następnie dodaj tradycyjną relację FK w drugiej lokalizacji do tabeli, która jest faktycznie kopią tylko do odczytu.

Możesz użyć wyzwalacza lub zaplanowanego zadania w lokalizacji podstawowej, aby aktualizować kopię.

Cade Roux
źródło
1
Re. „Możesz wyzwolić lub zaplanować zadanie w lokalizacji podstawowej, aby kopia była aktualizowana”: Dlaczego nie po prostu użyć replikacji SQL Server (w szczególności typu Transakcja vs. scalanie, ponieważ kopia subskrybenta (kopia, która ma tabele wymagające ograniczeń klucza obcego) musi być tylko do odczytu)? Zobacz: link
Tom
@Tom tak, z pewnością możesz użyć replikacji, aby zaktualizować kopię tabeli w zdalnej bazie danych.
Cade Roux
20

Możesz użyć ograniczenia sprawdzającego z funkcją zdefiniowaną przez użytkownika, aby dokonać sprawdzenia. Jest bardziej niezawodny niż spust. W razie potrzeby można go wyłączyć i ponownie włączyć, tak samo jak klucze obce i ponownie sprawdzić po przywróceniu bazy danych2.

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.
źródło
1
jest to lepsze rozwiązanie niż zaakceptowana odpowiedź i możesz również użyć go ponownie na wielu stołach
Milox,
3

Krótka odpowiedź jest taka, że ​​SQL Server (od wersji SQL 2008) nie obsługuje kluczy obcych między bazami danych - jak stwierdza komunikat o błędzie.

Chociaż nie możesz mieć deklaratywnej integralności referencyjnej (FK), możesz osiągnąć ten sam cel za pomocą wyzwalaczy. Jest to trochę mniej niezawodne, ponieważ logika, którą piszesz, może zawierać błędy, ale tak samo Cię tam zaprowadzi.

Zobacz dokumentację SQL @ http://msdn.microsoft.com/en-us/library/aa258254%28v=sql.80%29.aspx Który stan:

Wyzwalacze są często używane do wymuszania reguł biznesowych i integralności danych. SQL Server zapewnia deklaratywną integralność referencyjną (DRI) poprzez instrukcje tworzenia tabeli (ALTER TABLE i CREATE TABLE); jednak DRI nie zapewnia integralności referencyjnej między bazami danych. Aby wymusić integralność referencyjną (reguły dotyczące relacji między kluczem podstawowym i obcym tabel), użyj ograniczeń klucza podstawowego i obcego (słowa kluczowe PRIMARY KEY i FOREIGN KEY w ALTER TABLE i CREATE TABLE). Jeśli ograniczenia istnieją w tabeli wyzwalaczy, są one sprawdzane po wykonaniu wyzwalacza INSTEAD OF, a przed wykonaniem wyzwalacza AFTER. Jeśli ograniczenia zostaną naruszone, akcje wyzwalacza INSTEAD OF są wycofywane, a wyzwalacz AFTER nie jest wykonywany (uruchamiany).

Istnieje również dobra dyskusja w SQLTeam - http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=31135

EBarr
źródło
0

Jak mówi komunikat o błędzie, nie jest to obsługiwane na serwerze sql. Jedynym sposobem na zapewnienie integralności odniesienia jest praca z wyzwalaczami.

Jan
źródło
1
Czy możesz mi wyjaśnić na przykładzie
Sam