Добавить отношения внешнего ключа между двумя базами данных

91

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

Я попытался добавить его, и это дало мне следующую ошибку:

Msg 1763, уровень 16, состояние 0, строка 1
Ссылки на внешние ключи между базами данных не поддерживаются. Внешний ключ Database2.table2.

Сообщение 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
Это так неправильно ... Я просто надеюсь, что ОП понимает, что сам факт того, что он просит о чем-то подобном, является признаком того, что он, скорее всего, делает что-то не так ... не говоря уже о триггерах ...
MeTitus
1
@Marco Как я написал в своем ответе «Просто чтобы прояснить. Это не лучший подход с обеспечением ссылочной целостности. В идеале вам нужно, чтобы обе таблицы были в одной базе данных, но если это невозможно. Тогда приведенное выше является потенциальным решением для вы." Я объяснил, что это, вероятно, не очень хорошая идея.
Джон Хартсок
48

Если вам нужна надежная целостность, поместите обе таблицы в одну базу данных и используйте ограничение FK. Если ваша родительская таблица находится в другой базе данных, ничто не мешает кому-либо восстановить эту родительскую базу данных из старой резервной копии, и тогда у вас останутся сироты.

Вот почему FK между базами данных не поддерживается.

АК
источник
27

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

Затем добавьте традиционную связь FK во втором месте таблицы, которая фактически является копией только для чтения.

Вы можете использовать триггер или запланированное задание в основном расположении, чтобы обновлять копию.

Кейд Ру
источник
1
Re. «Вы можете запустить или запланировать задание в основном расположении, чтобы поддерживать копию в актуальном состоянии»: почему бы просто не использовать репликацию SQL Server (в частности, тип «Транзакция против слияния», так как копия подписчика (копия, в которой есть таблицы, требующие ограничений внешнего ключа) просто должен быть доступен только для чтения)? Смотрите: ссылка
Том
@Tom да, вы, безусловно, можете использовать репликацию, чтобы обновлять копию таблицы в удаленной базе данных.
Cade Roux
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. Если ограничения нарушаются, действия триггера INSTEAD OF откатываются, а триггер AFTER не выполняется (запускается).

В SQLTeam также есть обсуждение ОК - http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=31135

EBarr
источник
0

Как говорится в сообщении об ошибке, это не поддерживается на сервере sql. Единственный способ обеспечить референциальную целостность - работать с триггерами.

Янв
источник
1
Вы можете объяснить мне пример
Сэм