Agregar relación de clave externa entre dos bases de datos

90

Tengo dos tablas en dos bases de datos diferentes. En table1 (en database1) hay una columna llamada column1 y es una clave primaria. Ahora en la tabla2 (en la base de datos2) hay una columna llamada columna2 y quiero agregarla como una clave externa.

Intenté agregarlo y me dio el siguiente error:

Msg 1763, nivel 16, estado 0, línea 1
No se admiten las referencias de clave externa entre bases de datos. Clave externa Database2.table2.

Msg 1750, nivel 16, estado 0, línea 1
No se pudo crear restricción. Ver errores anteriores.

¿Cómo hago eso ya que las tablas están en diferentes bases de datos?

Sam
fuente

Respuestas:

84

Debería administrar la restricción de referencia en todas las bases de datos mediante un disparador.


Básicamente, crea un activador de inserción y actualización para verificar la existencia de la clave en la tabla de claves principales. Si la clave no existe, revierte la inserción o actualización y luego maneja la excepción.

Ejemplo:

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

Editado: Solo para aclarar. Este no es el mejor enfoque para hacer cumplir la integridad referencial. Idealmente, querría ambas tablas en la misma base de datos, pero si eso no es posible. Entonces lo anterior es una posible solución para usted.

John Hartsock
fuente
3
@John Hartsock: el ejemplo anterior puede fallar fácilmente sin agregar el manejo de transacciones adecuado. Se puede encontrar una discusión decente sobre el tipo de problema que puede ocurrir con "si no existe () entonces insertar" aquí - stackoverflow.com/questions/108403/…
EBarr
16
@John Hartsock: su solución tiene una laguna: si una de las dos bases de datos se restaura desde una copia de seguridad, los activadores no se activan, por supuesto. Así es como podemos acabar con filas huérfanas.
AK
4
@AlexKuznetsov Exactamente. Como expliqué, este no es el mejor enfoque, sino una posible solución.
John Hartsock
2
Esto está tan mal ... Solo espero que el OP se dé cuenta de que el solo hecho de que esté preguntando algo como esto es un síntoma de que lo más probable es que esté haciendo algo mal ... y mucho menos pensar en los desencadenantes ...
MeTitus
1
@Marco Como publiqué en mi respuesta "Solo para aclarar. Este no es el mejor enfoque para hacer cumplir la integridad referencial. Idealmente, querría ambas tablas en la misma base de datos, pero si eso no es posible. Entonces lo anterior es una posible solución para tú." Le expliqué que probablemente esto no sea una buena idea.
John Hartsock
48

Si necesita una integridad sólida como una roca, tenga ambas tablas en una base de datos y use una restricción FK. Si su tabla principal está en otra base de datos, nada impide que alguien restaure esa base de datos principal a partir de una copia de seguridad anterior, y luego tiene huérfanos.

Esta es la razón por la que no se admite FK entre bases de datos.

Alaska
fuente
27

En mi experiencia, la mejor manera de manejar esto cuando la principal fuente autorizada de información para dos tablas que están relacionadas tiene que estar en dos bases de datos separadas es sincronizar una copia de la tabla desde la ubicación principal a la ubicación secundaria (usando T- SQL o SSIS con la verificación de errores adecuada: no puede truncar y volver a llenar una tabla mientras tenga una referencia de clave externa, por lo que hay algunas formas de actualizar el gato en la tabla).

Luego agregue una relación FK tradicional en la segunda ubicación de la tabla, que es efectivamente una copia de solo lectura.

Puede utilizar un desencadenador o un trabajo programado en la ubicación principal para mantener la copia actualizada.

Cade Roux
fuente
1
Re. "Puede desencadenar o programar un trabajo en la ubicación principal para mantener la copia actualizada": ¿Por qué no usar la replicación de SQL Server (específicamente el tipo Transacción frente a Merge ya que la copia del suscriptor (la copia que tiene las tablas que necesitan restricciones de clave externa) simplemente necesita ser de solo lectura)? Ver: enlace
Tom
@Tom sí, ciertamente puede usar la replicación para mantener una copia de la tabla actualizada en una base de datos remota.
Cade Roux
20

Puede usar la restricción de verificación con una función definida por el usuario para realizar la verificación. Es más confiable que un disparador. Se puede deshabilitar y volver a habilitar cuando sea necesario, al igual que las claves externas y volver a verificar después de una restauración de la base de datos2.

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
fuente
1
esta es una solución mejor que la respuesta aceptada y también puede reutilizarla en varias tablas
Milox
3

La respuesta corta es que SQL Server (a partir de SQL 2008) no admite claves externas de bases de datos cruzadas, como indica el mensaje de error.

Si bien no puede tener integridad referencial declarativa (FK), puede alcanzar el mismo objetivo utilizando disparadores. Es un poco menos confiable, porque la lógica que escribe puede tener errores, pero lo llevará allí de todos modos.

Consulte los documentos SQL en http://msdn.microsoft.com/en-us/library/aa258254%28v=sql.80%29.aspx Qué estado:

Los activadores se utilizan a menudo para hacer cumplir las reglas comerciales y la integridad de los datos. SQL Server proporciona integridad referencial declarativa (DRI) a través de las sentencias de creación de tablas (ALTER TABLE y CREATE TABLE); sin embargo, DRI no proporciona integridad referencial entre bases de datos. Para hacer cumplir la integridad referencial (reglas sobre las relaciones entre las claves primaria y externa de las tablas), utilice restricciones de clave primaria y externa (las palabras clave PRIMARY KEY y FOREIGN KEY de ALTER TABLE y CREATE TABLE). Si existen restricciones en la tabla de desencadenadores, se verifican después de la ejecución del desencadenador INSTEAD OF y antes de la ejecución del desencadenador DESPUÉS. Si se violan las restricciones, las acciones del disparador INSTEAD OF se revierten y el disparador AFTER no se ejecuta (dispara).

También hay una buena discusión en SQLTeam - http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=31135

EBarr
fuente
0

Como dice el mensaje de error, esto no es compatible con el servidor SQL. La única forma de garantizar la integridad de referencia es trabajar con desencadenantes.

ene
fuente
1
¿Puede explicarme con un ejemplo
Sam