I'm trying, using the PostgreSQL Maestro tool, to reference a foreign key coming from a "local" DB to an other primary key inside an other DB (actually, they're both on the same remote machine).
I've heard about the postgres_fdw module to create a foreign table that act like a copy of the table inside the remote DB, but when I try to execute my query I have this error:
"SQL Error: ERROR: referenced relation "foreign_olo" is not a table".
This is my sql code:
CREATE TABLE edb.olo_config (
primary_key integer NOT NULL PRIMARY KEY,
puntamento varchar,
mail_contatto_to varchar,
mail_contatto_cc varchar,
/* Foreign keys */
CONSTRAINT olo_code
FOREIGN KEY (olo_code)
REFERENCES edb.foreign_olo(codice_operatore)
) WITH (
OIDS = FALSE
);
foreign_olo is my foreign table created with postgres_fdw.
I have tried to commit an INSERT or a simple SELECT on the foreign_olo table, and all went well, so I can't understand why for the foreign key case it can't be recognized as a table.
Thank you to everyone would give me an hand!
There are two parts to enforcing a foreign key constraint:
An INSERT (or UPDATE of the FK field) on the child table must check that a parent record exists.
A DELETE (or UPDATE of the PK field) on the parent table must check that no child record exists.
Foreign tables can't be referenced by FK constraints, because this second condition is impossible to enforce - there's no way for the remote server to automatically check for records in your local table.
You can implement the first check relatively easily with a trigger:
CREATE FUNCTION edb.olo_config_fk_trg() RETURNS TRIGGER AS
$$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM edb.foreign_olo
WHERE codice_operatore = new.olo_code
FOR KEY SHARE
)
THEN
RAISE foreign_key_violation
USING MESSAGE = 'edb.foreign_olo.codice_operatore="' || new.olo_code || '" not found';
END IF;
RETURN NULL;
END
$$
LANGUAGE plpgsql;
CREATE TRIGGER olo_config_fk_trg
AFTER INSERT OR UPDATE OF olo_code ON edb.olo_config
FOR EACH ROW EXECUTE PROCEDURE edb.olo_config_fk_trg();
You can create a similar trigger on the PK table to do the update/delete check, but it will require another foreign table in your other database which points back to your local olo_config.
Related
I'm having a really, really strange issue with postgres. I'm trying to generate GUIDs for business objects in my database, and I'm using a new schema for this. I've done this with several business objects already; the code I'm using here has been tested and has worked in other scenarios.
Here's the definition for the new table:
CREATE TABLE guid.public_obj
(
guid uuid NOT NULL DEFAULT uuid_generate_v4(),
id integer NOT NULL,
CONSTRAINT obj_guid_pkey PRIMARY KEY (guid),
CONSTRAINT obj_id_fkey FOREIGN KEY (id)
REFERENCES obj (obj_id)
ON UPDATE CASCADE ON DELETE CASCADE
)
However when I try to backfill this using the following code, I get a SQL state 23503 claiming that I'm violating the foreign key constraint.
INSERT INTO guid.public_obj (guid, id)
SELECT uuid_generate_v4(), o.obj_id
FROM obj o;
ERROR: insert or update on table "public_obj" violates foreign key constraint "obj_id_fkey"
SQL state: 23503
Detail: Key (id)=(-2) is not present in table "obj".
However, if I do a SELECT on the source table, the value is definitely present:
SELECT uuid_generate_v4(), o.obj_id
FROM obj o
WHERE obj_id = -2;
"0f218286-5b55-4836-8d70-54cfb117d836";-2
I'm baffled as to why postgres might think I'm violating the fkey constraint when I'm pulling the value directly out of the corresponding table. The only constraint on obj_id in the source table definition is that it's the primary key. It's defined as a serial; the select returns it as an integer. Please help!
Okay, apparently the reason this is failing is because unbeknownst to me the table (which, I stress, does not contain many elements) is partitioned. If I do a SELECT COUNT(*) FROM obj; it returns 348, but if I do a SELECT COUNT(*) FROM ONLY obj; it returns 44. Thus, there are two problems: first, some of the data in the table has not been partitioned correctly (there exists unpartitioned data in the parent table), and second, the data I'm interested in is split out across multiple child tables and the fkey constraint on the parent table fails because the data isn't actually in the parent table. (As a note, this is not my architecture; I'm having to work with something that's been around for quite some time.)
The partitioning is by implicit type (there are three partitions, each of which contains rows relating to a specific subtype of obj) and I think the eventual solution is going to be creating GUID tables for each of the subtypes. I'm going to have to handle the stuff that's actually in the obj table probably by selecting it into a temp table, dropping the rows from the obj table, then reinserting them so that they can be partitioned properly.
I'm adding delete cascading to a Table. The Clone table has a column DeviceID that is a foreign key to the Device table's DeviceID column. So the SQL script drops the original FK constraint, and attempts to add the new one:
IF EXISTS
(
SELECT *
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_NAME = 'FK_Clone_Device'
)
BEGIN
ALTER TABLE Clone
DROP CONSTRAINT FK_Clone_Device
END
IF NOT EXISTS
(
SELECT *
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_NAME = 'FK_Clone_Device_Cascade'
)
BEGIN
ALTER TABLE Clone
ADD CONSTRAINT FK_Clone_Device_Cascade
FOREIGN KEY (DeviceID) REFERENCES Device(DeviceID) ON DELETE CASCADE
END
When I run this script, I get the following error:
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_Clone_Device_Cascade". The conflict occurred in database "DevelopmentDB", table "dbo.Device", column 'DeviceID'.
Maybe I'm misunderstanding the error message, but it sounds like it's conflicting with itself. I'm confused that it happened on the Device table though.
There is an index in the Clone table on DeviceID. Would that matter?
This is on SQL SERVER R2 (Azure)
Sounds like you currently have data in the table that would violate the FK you are trying to create. One way to test this is to add "WITH (NOCHECK)" to the ALTER TABLE statement and see if it lets you create the constraint.
If it does let you create the constraint with NOCHECK, you can either leave it that way and the constraint will only be used to test future inserts/updates, or you can investigate your data to fix the FK violations.
So your example would be:
ALTER TABLE Clone WITH NOCHECK
ADD CONSTRAINT FK_Clone_Device_Cascade
FOREIGN KEY (DeviceID) REFERENCES Device(DeviceID) ON DELETE CASCADE
We have got table which has many rows. I want create foreign key between these two tables but
I get the following error.
'CMEvent' table saved successfully;
'BaseEvent' table
Unable to create relationship 'FK_CMEvent_Oid'.
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_CMEvent_Oid". The conflict occurred in database "CMO_RestoredData", table "dbo.BaseEvent", column 'Oid'.`
I was able to duplicate this error when I had a row in the CMEvent table that did not exist in BaseEvent.
Try running this query:
SELECT *
FROM CMEvent c
WHERE NOT EXISTS (
SELECT *
FROM BaseEvent
WHERE oid = c.oid )
If you get any rows back, these will have to be deleted before you can apply the foreign key constraint.
If you need to keep these orphaned rows, you can use WITH NOCHECK to only apply the constraint to new rows.
I know that a a temporary table will only exist for as long as a session of SQL Server is open, but why can't you have foreign key restraints on them?
Imagine this scenario: You create a foreign key relationship from your temp table to a concrete table's key. One of the restrictions on a foreign key relationship is that you cannot delete a row from a key table that is depended upon by your temp table. Now, generally when you create foreign key relationships you know to delete the dependent table rows before deleting the related rows in the key table, but how does a stored procedure or any other call into the database know to delete rows from your temp table? Not only is it impossible to discover spurious foreign key dependencies, other sessions could not reach your temp table even if it could discover the relationship. This leads to spurious failures in delete statements as foreign key constraints restrict the key table for dependent rows.
You can create foreign keys between tables in tempdb. For example, try this:
use tempdb
create table parent
(
parent_key int primary key clustered
)
create table child
(
child_key int primary key clustered,
child_parent_key int
)
alter table child add constraint fk_child_parent foreign key (child_parent_key) references parent(parent_key)
insert into parent(parent_key) select 1
insert into child(child_key, child_parent_key) select 1, 1
insert into child(child_key, child_parent_key) select 2, 2 -- this fails because of the FK constraint
drop table child
drop table parent
Could be because you can't have cross-database foreign key constraints and temp tables technically are created in the TempDB database.
Unless you mean between a temp table and another temp table... but really there's lots of issues you get into when you talk about those kind of constraints on a temp table.
I have the following code in a stored procedure.
....
select ... into #temp from ....
alter table #temp add constraint PK_mytemp13 primary key (....)
....
And I will get the following error message from time to time if the stored procedure is run in parallel.
There is already an object named 'PK_perf322dsf' in the database.
Could not create constraint. See previous errors.
I think it can be avoid by the following approaches. Is there any other more elegant solution?
Create a temp table with primary key first. Then insert the rows.
create table #temp (... primary key (....))
Create PK dynamically with session id dynamically.
declare #s varchar(500) = 'alter table #temp add constraint PK_temp' + ##spid + ' primary key (....)
This can only happen if the same client connection instantiation (which equals one SPID or connection in SQL Server) is being re-used for 2 different calls. Two parallel calls should have different connection instantiations and separate SPIDs
SPIDs are completely isolated from each other with local (single #temp tables)
Edit:
Ignore above
I've never named constraints on temp tables before. I use indexes as I need them or just add PRIMARY KEY after the column. Constraint names are database-unique in sys.objects
A PK is basically a non-unique clustered index. So use CREATE UNIQUE CLUSTERED INDEX instead as index names are unique per table in sys.indexes.
This fails when run in 2 SSMS Query windows
CREATE TABLE #gbn (foo int NOT NULL);
ALTER TABLE #gbn ADD CONSTRAINT PK_gbn PRIMARY KEY (foo);
Msg 2714, Level 16, State 5, Line 2
There is already an object named 'PK_gbn' in the database.
Msg 1750, Level 16, State 0, Line 2
Could not create constraint. See previous errors.
Curiously, the error and the constraint name match unlike your error
This works
CREATE TABLE #gbn (foo int NOT NULL);
CREATE UNIQUE CLUSTERED INDEX PK_gbn ON #gbn (foo);
I was trying to remember how to do this, but you can create a nameless primary key on a temp table and avoid this error. This is different than putting a column-level PK, as it supports more than 1 column. Here is an example:
CREATE TABLE #test
(
AccountNumber INT NOT NULL,
TransactionNumber INT NOT NULL,
PRIMARY KEY CLUSTERED (tranid, sys_process_dt)
);
This allows the end goal plus prevents name duplication. Querying will show that SQL Server will put a GUID in the name of the PK for you in sys.sysobjects:
SELECT *
FROM tempdb.sys.sysobjects
WHERE name LIKE '%#test%'
name | xtype
--------------------------------
#test___..._000000000407 | U
PK__#test_____B88A05A770B3A6A6 | PK
You can have your cake and eat it too.
you try to insert to the same temporary table from different connections (which is impossible, instead of global temp tables),
or you try to insert into different tables.
if 2nd - you simply may do the following - ALTER TABLE #temp ADD PRIMARY KEY(...)
if 1st - you have to create the table (regular or global temporary) with key prior to use it in parallel operations
I know this has been answered and accepted but still did not see the correct point has been call out.
when you create Named constraint, name of the constraint has to be precise at table level. they are scoped at database level. so either do not create named constrained and let sql pick its own name or if you give name make sure it s unique in that DB. even for TEMP DB.
Even more to the point, ANY named constraint on a temp table must be uniquely named across sessions. This also means that a default constraint (say you're setting a field to default to a value of -1) and that default constraint is explicitly named, SQL Server will throw an error when you attempt to create a temp table in another session window even though it's not a key constraint.
For example, open two query windows and run the following code in one and then the other (without closing either window first):
DROP TABLE IF EXISTS #TempTbl1;
CREATE TABLE #TempTbl_1 (
[TestCol1] INT
,[TestCol2] BIGINT CONSTRAINT [DF_Tc2] DEFAULT (-1)
);
The first window will execute fine, but the second will throw an error stating
Msg 2714, Level 16, State 5, Line 2
There is already an object named 'DF_Tc2' in the database.
Msg 1750, Level 16, State 1, Line 2
Could not create constraint or index. See previous errors.
This is in spite of the fact that the table has no PKs or indexes, just a named default constraint.