Add Foreign Key relationship between two Databases - sql

I have two tables in two different databases. In table1 (in database1) there is a column called column1 and it is a primary key. Now in table2 (in database2) there is a column called column2 and I want to add it as a foreign key.
I tried to add it and it gave me the following error:
Msg 1763, Level 16, State 0, Line 1
Cross-database foreign key references are not supported. Foreign key Database2.table2.
Msg 1750, Level 16, State 0, Line 1
Could not create constraint. See previous errors.
How do I do that since the tables are in different databases.

You would need to manage the referential constraint across databases using a Trigger.
Basically you create an insert, update trigger to verify the existence of the Key in the Primary key table. If the key does not exist then revert the insert or update and then handle the exception.
Example:
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
Edited: Just to clarify. This is not the best approach with enforcing referential integrity. Ideally you would want both tables in the same db but if that is not possible. Then the above is a potential work around for you.

If you need rock solid integrity, have both tables in one database, and use an FK constraint. If your parent table is in another database, nothing prevents anyone from restoring that parent database from an old backup, and then you have orphans.
This is why FK between databases is not supported.

You could use check constraint with a user defined function to make the check. It is more reliable than a trigger. It can be disabled and reenabled when necessary same as foreign keys and rechecked after a database2 restore.
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)

In my experience, the best way to handle this when the primary authoritative source of information for two tables which are related has to be in two separate databases is to sync a copy of the table from the primary location to the secondary location (using T-SQL or SSIS with appropriate error checking - you cannot truncate and repopulate a table while it has a foreign key reference, so there are a few ways to skin the cat on the table updating).
Then add a traditional FK relationship in the second location to the table which is effectively a read-only copy.
You can use a trigger or scheduled job in the primary location to keep the copy updated.

The short answer is that SQL Server (as of SQL 2008) does not support cross database foreign keys--as the error message states.
While you cannot have declarative referential integrity (the FK), you can reach the same goal using triggers. It's a bit less reliable, because the logic you write may have bugs, but it will get you there just the same.
See the SQL docs # http://msdn.microsoft.com/en-us/library/aa258254%28v=sql.80%29.aspx Which state:
Triggers are often used for enforcing
business rules and data integrity. SQL
Server provides declarative
referential integrity (DRI) through
the table creation statements (ALTER
TABLE and CREATE TABLE); however, DRI
does not provide cross-database
referential integrity. To enforce
referential integrity (rules about the
relationships between the primary and
foreign keys of tables), use primary
and foreign key constraints (the
PRIMARY KEY and FOREIGN KEY keywords
of ALTER TABLE and CREATE TABLE). If
constraints exist on the trigger
table, they are checked after the
INSTEAD OF trigger execution and prior
to the AFTER trigger execution. If the
constraints are violated, the INSTEAD
OF trigger actions are rolled back and
the AFTER trigger is not executed
(fired).
There is also an OK discussion over at SQLTeam - http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=31135

Achieving referential integrity accross databases is not an easy task.
Here is a list of frequently employed mechanisms:
Clone & Sync: The referenced data is regularly cloned/merged into the referencing database. This may be suitable if the referenced data is rarely changing. You end up with two physical copies of the same data, and need a reliable process to keep them in sync (e.g. with an ETL pipeline).
Triggers: Changes to the referencing data and the referenced data are caught by SQL triggers, which ensure referential integrity. However, triggers can be slow, and may not fire at a database restore. It cannot hurt to run scheduled consistency checks as part of the operations monitoring. Write access to the referenced database is required for installing and maintaining the trigger.
Check constraints: SQL-Server offers user-defined contraints, which ensure that every row satisfies a given condition. One can exploit this functionality by writing a user defined function that checks the existence of a row in the referenced data, and then use this function as a CHECK's predicate in the referencing table. This does not catch changes in the referenced data. It is an RDBMS-specific solution, but works accross server boundaries (e.g. using linked servers). It is a good choice for referencing globally unique IDs, such as article codes in a company's ERP system, which never get deleted or re-assigned.
Re-think database architecture: When all the above mechanisms are unsatisfactory, multiple databases may be merged in a single database. The originating database names can become schema names, allowing effective grouping of database objects.

As the error message says, this is not supported on sql server.
The only way to ensure refrerential integrity is to work with triggers.

Related

SQL Server 2016 Cannot add system versioning to relationship table

The SQL Server 2016 system versioning is cool. I am using the free Developer version. Thanks MS!
I am unable to figure out if it will give me versioning of many to many relationships. I have a User object that has a collection of Roles and vice versa. Entity Framework has generated the UserRoles table that holds the relationship between User and Roles. I was able to turn on system versioning for the User and Roles tables using this article http://sqlhints.com/tag/modify-existing-table-as-system-versioned-temporal-table/.
But, I am not able to turn on for UserRoles. I get an error
Setting SYSTEM_VERSIONING to ON failed because table has a FOREIGN KEY with cascading DELETE or UPDATE.
Does this mean we cannot know the versioning for many-many relationships?
For eg.
on 6/1 - User1 had role1 and role2, but
on 6/4 - User1's role changed to role1 and role3
So, if I wanted to know the state of the user on 6/1, I thought that's possible only by turning on system versioning on UserRoles, but that's not working.
Is this doable or not supported by SQL Server 2016? If not, is there any other way this can be accomplished?
It's important to notice that the limitation of using CASCADE on FOREIGN KEY constraints in temporal tables is applicable only to SQL Server 2016. In SQL Server 2017, this limitation doesn't exist anymore.
This is the relevant part from the official documentation:
ON DELETE CASCADE and ON UPDATE CASCADE are not permitted on the
current table. In other words, when temporal table is referencing
table in the foreign key relationship (corresponding to
parent_object_id in sys.foreign_keys) CASCADE options are not allowed.
To work around this limitation, use application logic or after
triggers to maintain consistency on delete in primary key table
(corresponding to referenced_object_id in sys.foreign_keys). If
primary key table is temporal and referencing table is non-temporal,
there's no such limitation.
> NOTE: This limitation applies to SQL Server 2016 only. CASCADE options
are supported in SQL Database and SQL Server 2017 starting from CTP
2.0.
Sounds like it's the ON UPDATE CASCADE or ON UPDATE DELETE foreign key that's the issue. Remove the cascading and replace that with a delete proc that knows and handles the proper relationships and you should be fine.
Personally, I like knowing what my deletes/updates are doing rather than trusting the relationships to handle all of them. I can see potential locking issues as well as know that there are times I really want to prevent an update or delete rather than letting it cascade through all of the tables unseen.
ON DELETE CASCADE and ON UPDATE CASCADE are not permitted on the current table. In other words, when temporal table is referencing table in the foreign key relationship (corresponding to parent_object_id in sys.foreign_keys) CASCADE options are not allowed. To work around this limitation, use application logic or after triggers to maintain consistency on delete in primary key table (corresponding to referenced_object_id in sys.foreign_keys). If primary key table is temporal and referencing table is non-temporal, there’s no such limitation.

PostgreSQL FOREIGN KEY with second database

I'm running the following queries on PostgreSQL 9.3:
CREATE TABLE "app_item"
(
"id" SERIAL NOT NULL PRIMARY KEY,
"location_id" UUID NOT NULL
);
CREATE INDEX app_item_e274a5da
ON "app_item" ("location_id");
ALTER TABLE "app_item"
ADD CONSTRAINT app_item_location_id_5cecc1c0b46e12e2_fk_fias_addrobj_aoguid
FOREIGN KEY ("location_id") REFERENCES "fias_addrobj" ("aoguid") deferrable
initially deferred;
Third query returns:
ERROR: relation "fias_addrobj" does not exist
app_item - table in first database
fias_addrobj - table in second database
How to do correct query with this databases?
A local table must be referenced
However, as stated within the below link, you could maybe use a trigger which uses a cross server join (facilitated by dblink) to simulate the built-in methods for constraining?
For instance, you could have a trigger set up that on INSERT, checks to see if a given FK exists to aid with enforcing referential integrity, or on DELETE to cascade
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=101322
P.S. Would avoid this at all costs.
I've not had occasion to use this myself, but you might want to look into Foreign Data Wrappers, which are essentially the successor to dblink. In particular, postgres-fdw.
Once the general setup of the fdw is in place (steps 1-3 in the link above), you could create a foreign table via CREATE FOREIGN TABLE, defined like the table in your remote DB, and then use that table as part of the foreign key CONSTRAINT, and see if it works.
If that doesn't work, another option would be to have a process which ETL's the data (say, via a Python script) from the remote server over to the local server (say, on an hourly or daily basis, depending on the size), and then you would have a true local table to use in the foreign key CONSTRAINT. It wouldn't be real-time, but depending on your needs, may suffice.

SQL Foreign Key in Another Database

I am currently creating a database at work that is essentially a child of another database. When our clients get our software they get a database, lets call it MasterBase. This database contains schema of two different smaller databases put together, lets call them MasterC and MasterF.
My job is to take all the schema that pertain to MasterF and put them in their own database. The problem is there are a lot of references to tables and views in MasterC. To work around this I will add a property that will pair MasterF with MasterC so that in my .NET solution I should be able to do things like:
Table tableName.... (table being created in MasterF)
References .... MasterC.dbo.tableC (table in MasterC)
However, in SQL this raises the error: MSG 1763 Cross-Database foreign key references are not supported.
I have read the most common work around is to add a trigger, but seeings how this is not that safe and there are MANY instances of this I was wondering if there was another way, perhaps a stored procedure or something of the sort.
If a trigger is really the best/only way and I MUST add a trigger to every table that has this problem, how would I go about writing this trigger, I know a little SQL but am hardly proficient.
Please Help!
Triggers (as you mention)...
Database Partitioning...
Duplicate the tables from MasterC (are they transactional? How often are they updated? Does MasterC need the updates? If so, can you allow for the copies to be updated every 24-hours, etc. via a "Job"?)
Per your last comment about individual builds, I guess I'd say that the threshold for when a client "needs" both is when they...well, "need" both. Sounds almost like you need a 3rd schema, for tables in MasterC that do NOT reference MasterF, for those clients you're categorizing as not needing both.
BTW -- this is all about referential integrity, yes? There are other strategies you could employ toward that goal, besides 'relationships'.
EDIT
CREATE TRIGGER myTrigger ON myTable
AFTER INSERT
AS
IF NOT EXISTS (SELECT * FROM OtherDatabase.otherschema.othertable F
JOIN inserted AS i
ON F.KeyYouAreLookingFor = i.KeyYouHave)
BEGIN
RAISERROR ('Lookup Value Not Found -- Insert Failed', 16, 1);
ROLLBACK TRANSACTION;
RETURN
END;

Add Foreign Key Contraint between Tables in different Databases [duplicate]

I have two tables in two different databases. In table1 (in database1) there is a column called column1 and it is a primary key. Now in table2 (in database2) there is a column called column2 and I want to add it as a foreign key.
I tried to add it and it gave me the following error:
Msg 1763, Level 16, State 0, Line 1
Cross-database foreign key references are not supported. Foreign key Database2.table2.
Msg 1750, Level 16, State 0, Line 1
Could not create constraint. See previous errors.
How do I do that since the tables are in different databases.
You would need to manage the referential constraint across databases using a Trigger.
Basically you create an insert, update trigger to verify the existence of the Key in the Primary key table. If the key does not exist then revert the insert or update and then handle the exception.
Example:
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
Edited: Just to clarify. This is not the best approach with enforcing referential integrity. Ideally you would want both tables in the same db but if that is not possible. Then the above is a potential work around for you.
If you need rock solid integrity, have both tables in one database, and use an FK constraint. If your parent table is in another database, nothing prevents anyone from restoring that parent database from an old backup, and then you have orphans.
This is why FK between databases is not supported.
You could use check constraint with a user defined function to make the check. It is more reliable than a trigger. It can be disabled and reenabled when necessary same as foreign keys and rechecked after a database2 restore.
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)
In my experience, the best way to handle this when the primary authoritative source of information for two tables which are related has to be in two separate databases is to sync a copy of the table from the primary location to the secondary location (using T-SQL or SSIS with appropriate error checking - you cannot truncate and repopulate a table while it has a foreign key reference, so there are a few ways to skin the cat on the table updating).
Then add a traditional FK relationship in the second location to the table which is effectively a read-only copy.
You can use a trigger or scheduled job in the primary location to keep the copy updated.
The short answer is that SQL Server (as of SQL 2008) does not support cross database foreign keys--as the error message states.
While you cannot have declarative referential integrity (the FK), you can reach the same goal using triggers. It's a bit less reliable, because the logic you write may have bugs, but it will get you there just the same.
See the SQL docs # http://msdn.microsoft.com/en-us/library/aa258254%28v=sql.80%29.aspx Which state:
Triggers are often used for enforcing
business rules and data integrity. SQL
Server provides declarative
referential integrity (DRI) through
the table creation statements (ALTER
TABLE and CREATE TABLE); however, DRI
does not provide cross-database
referential integrity. To enforce
referential integrity (rules about the
relationships between the primary and
foreign keys of tables), use primary
and foreign key constraints (the
PRIMARY KEY and FOREIGN KEY keywords
of ALTER TABLE and CREATE TABLE). If
constraints exist on the trigger
table, they are checked after the
INSTEAD OF trigger execution and prior
to the AFTER trigger execution. If the
constraints are violated, the INSTEAD
OF trigger actions are rolled back and
the AFTER trigger is not executed
(fired).
There is also an OK discussion over at SQLTeam - http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=31135
Achieving referential integrity accross databases is not an easy task.
Here is a list of frequently employed mechanisms:
Clone & Sync: The referenced data is regularly cloned/merged into the referencing database. This may be suitable if the referenced data is rarely changing. You end up with two physical copies of the same data, and need a reliable process to keep them in sync (e.g. with an ETL pipeline).
Triggers: Changes to the referencing data and the referenced data are caught by SQL triggers, which ensure referential integrity. However, triggers can be slow, and may not fire at a database restore. It cannot hurt to run scheduled consistency checks as part of the operations monitoring. Write access to the referenced database is required for installing and maintaining the trigger.
Check constraints: SQL-Server offers user-defined contraints, which ensure that every row satisfies a given condition. One can exploit this functionality by writing a user defined function that checks the existence of a row in the referenced data, and then use this function as a CHECK's predicate in the referencing table. This does not catch changes in the referenced data. It is an RDBMS-specific solution, but works accross server boundaries (e.g. using linked servers). It is a good choice for referencing globally unique IDs, such as article codes in a company's ERP system, which never get deleted or re-assigned.
Re-think database architecture: When all the above mechanisms are unsatisfactory, multiple databases may be merged in a single database. The originating database names can become schema names, allowing effective grouping of database objects.
As the error message says, this is not supported on sql server.
The only way to ensure refrerential integrity is to work with triggers.

TRUNCATE TABLE query unable to execute in SSIS because of foreign key

I have a table Person which contains 2 fields.In my another database i have a Participant Table(also have 2 columns).From Participant Table i have to insert values into Person Table.
but before every insertion,i want truncate the person Table.
I have try it out with linking Execute Sql task to Data flow task.But it is showing error that a Primary Foreign key relation exists there.
If I understand correctly, SSIS has nothing to do with your real problem, which is that you want to truncate a table that is referenced by a foreign key constraint. That question has already been answered: Cannot truncate table because it is being referenced by a FOREIGN KEY constraint?
If a table in sql server has foreign key references then you can't truncate. instead in your execute sql task use delete without a where clause.
delete from person_table
If you are really adamant about truncating the table, you could drop the foreign key constraints, then truncate the table then recreate the foreign key constraints. Providing of course, that the user you are running the package as has the privileges to do so.
Create an "Execute SQl" task and run DELETE FROM person
after this task, run your import.
DELETE FROM will give the same result as TRUNCATE TABLE, but if the table has a foreign key pointing to it, it cant be truncated. You have to use the delete command
You won't be able to delete either unless cascading deletes is turned on (or you delete the child records first). Why is this a problem you ask, why can't I do what I want to do? Because if you do then you may lose the integrity of the data. Suppose I have records in table2 which relate to records in table 1. Suppose further that table1 has an autogenerated id. If I could truncate that table, then I leave those records i ntable 2 hanging out without any record to reference them, they have become orphaned. Well but I'm putting the data back in you say. But then they will have new id numbers and you will still lose the relatinoship tothe related data.
Can you drop the FK and truncate and insert and recreate the FK. Yes you can but it is a poor practice and you should not unless you are also recreating those related records.
The best practice is to use a MERGE statement to update or insert depending on what you need.
In SSIS Transfer SQL Server Objects Task Set Property DeleteFirst to TRUE