İki Veritabanı arasında Yabancı Anahtar ilişkisi ekleyin


92

İki farklı veritabanında iki tablom var. Tablo1'de (veritabanı1'de) sütun1 adında bir sütun vardır ve bu bir birincil anahtardır. Şimdi tablo2'de (veritabanı2'de) column2 adında bir sütun var ve bunu yabancı anahtar olarak eklemek istiyorum.

Eklemeye çalıştım ve bana şu hatayı verdi:

Msg 1763, Düzey 16, Durum 0, Satır 1
Veritabanları arası yabancı anahtar başvuruları desteklenmez. Yabancı anahtar Veritabanı2. Tablo2.

Msg 1750, Düzey 16, Durum 0, Satır 1
Kısıtlama yaratılamadı. Önceki hatalara bakın.

Tablolar farklı veritabanlarında olduğu için bunu nasıl yaparım.

Yanıtlar:


84

Bir Tetikleyici kullanarak veritabanları genelinde referans kısıtlamasını yönetmeniz gerekir.


Temel olarak, Anahtarın Birincil anahtar tablosundaki varlığını doğrulamak için bir ekleme, güncelleme tetikleyicisi oluşturursunuz. Anahtar yoksa, ekleme veya güncellemeyi geri alın ve ardından istisnayı işleyin.

Misal:

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

Düzenlendi: Sadece açıklığa kavuşturmak için. Bilgi tutarlılığını zorunlu kılan en iyi yaklaşım bu değildir. İdeal olarak, her iki tablonun da aynı veritabanında olmasını istersiniz, ancak bu mümkün değilse. O halde yukarıdakiler sizin için potansiyel bir çalışmadır.


4
@John Hartsock - yukarıdaki örnek, uygun işlem işlemleri eklenmeden kolayca başarısız olabilir. "Eğer yoksa () sonra ekleyiniz" ile ortaya çıkabilecek sorun türü hakkında iyi bir tartışma burada bulunabilir - stackoverflow.com/questions/108403/…
EBarr

17
@John Hartsock - çözümünüzde bir boşluk var: iki veritabanından biri bir yedeklemeden geri yüklenirse, tetikleyiciler elbette çalışmaz. Yetim sıraları bu şekilde biter.
AK

4
@AlexKuznetsov Kesinlikle. Açıkladığım gibi, bu en iyi yaklaşım değil, potansiyel bir çözümdür.
John Hartsock

2
Bu çok yanlış ... Umarım OP sadece böyle bir şeyi sormasının, büyük olasılıkla yanlış bir şey yaptığına dair bir semptom olduğunu anlar ... tetikleyicileri düşünmek şöyle dursun ...
MeTitus

1
@Marco Cevabımda Yayınladığım Gibi "Sadece açıklığa kavuşturmak için. Bu, referans bütünlüğünü zorlamak için en iyi yaklaşım değildir. İdeal olarak, her iki tabloyu da aynı veritabanında istersiniz, ancak bu mümkün değilse. O zaman yukarıdakiler, sen." Bunun muhtemelen iyi bir fikir olmadığını açıkladım.
John Hartsock

48

Çok sağlam bir bütünlüğe ihtiyacınız varsa, her iki tabloyu tek bir veritabanında bulundurun ve bir FK kısıtlaması kullanın. Üst tablonuz başka bir veritabanındaysa, hiçbir şey kimsenin bu ana veritabanını eski bir yedekten geri yüklemesini engellemez ve sonra öksüzler olur.

Veritabanları arasında FK'nin desteklenmemesinin nedeni budur.


27

Tecrübelerime göre, birbiriyle ilişkili iki tablo için birincil yetkili bilgi kaynağının iki ayrı veritabanında olması gerektiğinde, bunu halletmenin en iyi yolu, tablonun bir kopyasını birincil konumdan ikincil konuma senkronize etmektir (T- Uygun hata denetimi ile SQL veya SSIS - bir yabancı anahtar referansı varken bir tabloyu kısaltamaz ve yeniden dolduramazsınız, bu nedenle tablo güncellemesinde kediyi kaplamanın birkaç yolu vardır).

Daha sonra, ikinci konumda, etkin bir şekilde salt okunur bir kopya olan geleneksel bir FK ilişkisini tabloya ekleyin.

Kopyayı güncel tutmak için birincil konumda bir tetikleyici veya planlanmış bir iş kullanabilirsiniz.


1
Yeniden. "Kopyayı güncel tutmak için birincil konumda işi tetikleyebilir veya zamanlayabilirsiniz": Neden yalnızca SQL Server Replication kullanılmıyorsunuz (özellikle Abonenin kopyasından (Yabancı Anahtar Kısıtlamalarına ihtiyaç duyan Tablolara sahip kopya) beri İşlem ve Birleştirme türü kullanılmıyor salt okunur olması gerekir)? Bakınız: bağlantı
Tom

@Tom evet, tablonun bir kopyasını uzak bir veritabanında güncel tutmak için kesinlikle replikasyonu kullanabilirsiniz.
Cade Roux

21

Kontrol yapmak için kullanıcı tanımlı bir fonksiyonla kontrol kısıtlamasını kullanabilirsiniz. Bir tetikleyiciden daha güvenilirdir. Yabancı anahtarlarla aynı şekilde gerektiğinde devre dışı bırakılabilir ve yeniden etkinleştirilebilir ve bir veritabanı2 geri yüklemesinden sonra yeniden kontrol edilebilir.

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
bu, kabul edilen cevaptan daha iyi bir çözümdür ve birden fazla masada da tekrar kullanabilirsiniz
Milox

3

Kısa cevap, SQL Server'ın (SQL 2008 itibariyle), hata mesajında ​​belirtildiği gibi, çapraz veritabanı yabancı anahtarlarını desteklememesidir.

Bildirim temelli referans bütünlüğüne (FK) sahip olamamakla birlikte, tetikleyicileri kullanarak aynı hedefe ulaşabilirsiniz. Biraz daha az güvenilir çünkü yazdığınız mantıkta hatalar olabilir, ancak sizi oraya aynı şekilde ulaştıracaktır.

SQL belgelerine bakın @ http://msdn.microsoft.com/en-us/library/aa258254%28v=sql.80%29.aspx Hangi durum:

Tetikleyiciler genellikle iş kurallarını ve veri bütünlüğünü uygulamak için kullanılır. SQL Server, tablo oluşturma deyimleri (ALTER TABLE ve CREATE TABLE) aracılığıyla bildirim temelli başvuru bütünlüğü (DRI) sağlar; ancak DRI, veritabanları arası bilgi bütünlüğü sağlamaz. Bilgi tutarlılığını zorlamak için (tabloların birincil ve yabancı anahtarları arasındaki ilişkiler hakkındaki kurallar), birincil ve yabancı anahtar kısıtlamalarını (ALTER TABLE ve CREATE TABLE için PRIMARY KEY ve FOREIGN KEY anahtar sözcükleri) kullanın. Tetikleme tablosunda kısıtlamalar mevcutsa, tetiklemenin INSTEAD OF çalıştırılmasından sonra ve AFTER tetiklemenin yürütülmesinden önce kontrol edilirler. Kısıtlamalar ihlal edilirse, INSTEAD OF tetikleme eylemleri geri alınır ve AFTER tetikleme yürütülmez (tetiklenmez).

SQLTeam'de de bir OK tartışması var - http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=31135


0

Hata mesajının dediği gibi, bu sql sunucusunda desteklenmiyor. Refreransiyel bütünlüğü sağlamanın tek yolu, tetikleyicilerle çalışmaktır.


1
Bana bir örnekle açıklayabilir misin
Sam
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.