Додайте взаємозв'язок зовнішнього ключа між двома базами даних


91

У мене є дві таблиці в двох різних базах даних. У таблиці1 (у базі даних1) є стовпець з назвою column1 і це первинний ключ. Тепер у таблиці2 (у базі даних2) є стовпець із назвою column2, і я хочу додати його як зовнішній ключ.

Я спробував додати його, і це дало мені таку помилку:

Повідомлення 1763, рівень 16, стан 0, рядок 1
Посилання на зовнішні ключі між базами даних не підтримуються. База даних зовнішнього ключа 2. таблиця2.

Повідомлення 1750, рівень 16, стан 0, рядок 1
Не вдалося створити обмеження. Див. Попередні помилки.

Як це зробити, оскільки таблиці знаходяться в різних базах даних.

Відповіді:


84

Вам потрібно було б керувати посилальним обмеженням у базах даних за допомогою тригера.


В основному ви створюєте вставку, тригер оновлення, щоб підтвердити існування ключа в таблиці первинного ключа. Якщо ключ не існує, поверніть вставку або оновлення, а потім обробіть виняток.

Приклад:

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

Відредаговано: Просто для уточнення. Це не найкращий підхід із забезпеченням посилальної цілісності. В ідеалі ви хотіли б, щоб обидві таблиці були в однаковій базі даних, але якщо це неможливо. Тоді вищезазначене є потенційною роботою для вас.


4
@ John Hartsock - наведений вище приклад може легко вийти з ладу, не додаючи належної обробки транзакцій. Пристойний обговорення типу проблеми , які можуть виникнути з «якщо не існує () , а потім вставити» можна знайти тут - stackoverflow.com/questions/108403 / ...
EBarr

17
@ John Hartsock - у вашому рішенні є лазівка: якщо одна з двох баз даних відновлюється з резервної копії, тригери, звичайно, не спрацьовують. Ось так ми можемо закінчити сирітські ряди.
AK

4
@AlexKuznetsov Рівно. Як я пояснив, це не найкращий підхід, а потенційна робота.
Джон Хартсок,

2
Це просто настільки неправильно ... Я просто сподіваюся, що ОП усвідомлює, що саме той факт, що він просить про щось подібне, є симптомом того, що він, швидше за все, робить щось не так ... не кажучи вже про те, щоб
запустити

1
@Marco Як я вже публікував у своїй відповіді "Просто для уточнення. Це не найкращий підхід із забезпеченням посилальної цілісності. В ідеалі ви хотіли б, щоб обидві таблиці були в одній і тій же базі даних, але якщо це неможливо. Тоді вищезазначене є потенційною роботою ви." Я пояснив, що це, мабуть, не є гарною ідеєю.
Джон Хартсок,

48

Якщо вам потрібна міцна цілісність, майте обидві таблиці в одній базі даних і використовуйте обмеження FK. Якщо ваша батьківська таблиця знаходиться в іншій базі даних, ніщо не заважає нікому відновити цю батьківську базу даних зі старої резервної копії, і тоді у вас є сироти.

Ось чому FK між базами даних не підтримується.


27

На моєму досвіді, найкращий спосіб вирішити це, коли основне авторитетне джерело інформації для двох пов'язаних таблиць повинно знаходитись у двох окремих базах даних - це синхронізувати копію таблиці з основного розташування у вторинне розташування (за допомогою T- SQL або SSIS із відповідною перевіркою помилок - ви не можете скоротити та повторно заповнити таблицю, поки у неї є посилання на зовнішній ключ, тому існує кілька способів очищення кота від столу для оновлення).

Потім додайте традиційне відношення FK у другому розташуванні до таблиці, яка фактично є копією лише для читання.

Ви можете використовувати тригер або заплановане завдання в основному розташуванні, щоб копія оновлювалася.


1
Re. "Ви можете активувати або запланувати завдання в основному місці, щоб постійно оновлювати копію": Чому б просто не використовувати реплікацію SQL Server (зокрема тип транзакції проти злиття, оскільки копія абонента (копія, яка містить таблиці, що потребують обмежень зовнішнього ключа) потрібно лише для читання)? Дивіться: посилання
Том

@ Том так, ви, звичайно, можете використовувати реплікацію, щоб постійно оновлювати копію таблиці у віддаленій базі даних.
Кейд Ру

21

Ви можете використовувати обмеження перевірки з визначеною користувачем функцією для здійснення перевірки. Це надійніше спускового гачка. Його можна вимкнути та повторно включити за необхідності, як і зовнішні ключі, і перевірити після відновлення бази даних2.

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
це краще рішення, ніж прийнята відповідь, і ви також можете повторно використовувати його на кількох таблицях
Milox,

3

Коротка відповідь полягає в тому, що SQL Server (станом на SQL 2008) не підтримує зовнішні ключі міжбазової бази даних - як зазначено в повідомленні про помилку.

Хоча ви не можете мати декларативну посилальну цілісність (FK), ви можете досягти тієї ж мети за допомогою тригерів. Це трохи менш надійно, оскільки логіка, яку ви пишете, може мати помилки, але вона приведе вас туди ж однаково.

Див. SQL-документи @ http://msdn.microsoft.com/en-us/library/aa258254%28v=sql.80%29.aspx Який стан:

Тригери часто використовуються для забезпечення ділових правил та цілісності даних. SQL Server забезпечує декларативну посилальну цілісність (DRI) через оператори створення таблиці (ALTER TABLE і CREATE TABLE); однак DRI не забезпечує референційну цілісність між базами даних. Для забезпечення посилальної цілісності (правила щодо взаємозв’язків між первинним та зовнішнім ключами таблиць) використовуйте обмеження первинного та зовнішнього ключів (ключові слова PRIMARY KEY та FOREIGN KEY для ALTER TABLE та CREATE TABLE). Якщо в таблиці тригера існують обмеження, вони перевіряються після виконання тригера ЗАМІСТ І перед виконанням тригера ПІСЛЯ. Якщо обмеження порушені, дії тригера INSTEAD OF відкочуються, а тригер AFTER не виконується (спрацьовує).

Також існує нормальна дискусія на SQLTeam - http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=31135


0

Як сказано в повідомленні про помилку, це не підтримується на сервері sql. Єдиний спосіб забезпечити цілісність посилання - це робота з тригерами.


1
Чи можете ви пояснити мене на прикладі
Сем
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.