deferrable initially deferred in postgresql - sql

I have a cyclic foreign keys on 2 tables, so i use deferrable initially deferred as below:
uni=# create table vorlesungen (vnr integer primary key, gelesenvon integer);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "vorlesungen_pkey" for table "vorlesungen"
CREATE TABLE
uni=# create table professoren (pnr integer primary key, lieblingsvo integer);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "professoren_pkey" for table "professoren"
CREATE TABLE
uni=# alter table professoren add constraint vfk foreign key (lieblingsvo) references vorlesungen (vnr) deferrable initially deferred;
ALTER TABLE
uni=# alter table vorlesungen add constraint pfk foreign key (gelesenvon) references professoren (pnr) deferrable initially deferred;
ALTER TABLE
so far so good.
but now when i want to insert into the tables, i get foreign key violations, although i specified deferrable initially deferred:
uni=# insert into vorlesungen values (1, 1);
ERROR: insert or update on table "vorlesungen" violates foreign key constraint "pfk"
DETAIL: Key (gelesenvon)=(1) is not present in table "professoren".
uni=# insert into professoren values (1, 1);
ERROR: insert or update on table "professoren" violates foreign key constraint "vfk"
DETAIL: Key (lieblingsvo)=(1) is not present in table "vorlesungen".
whats the problem?

Are you explicitly opening a transaction before the
INSERTs? If you do not use BEGIN, each insert is an independent transaction, therefore enforcing the foreign keys at the end of each command.

Related

Database contains foreign key that doesn exist even though there is a foreign key constraint?

I am working with a database where there is a foreign key ID that doesn't exist even though there is a foreign key constraint.
There is a table called "Workplace" with a foreign key column called "AddressID" pointing to another table called "Address"
The foreign key is as follows:
ALTER TABLE [dbo].[Workplace] WITH NOCHECK ADD CONSTRAINT [FK_Workplace_Address] FOREIGN KEY([AddressID])
REFERENCES [dbo].[Address] ([ID])
GO
ALTER TABLE [dbo].[Workplace] CHECK CONSTRAINT [FK_Workplace_Address]
GO
For one particular row in the Workplace table, the AddressID has a value of "1".
When I run select * from Address where ID = 1 there is no result.
Then I ran update Workplace set AddressID = 3 where Workplace.ID = 20 the value changed to 3 and I have verified that an Address with ID 3 exists.
Then I ran update Workplace set AddressID = 1 where Workplace.ID = 20 again and I get the error
The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_Workplace_Address". The conflict occurred in database db_name, table "dbo.Address", column 'ID'.
I don't understand how the value 1 could have been put there in the first place. The Address with ID=1 couldn't have been deleted after the constraint was put in place. The constraint creation would also fail if the record was deleted in the first place. Does anyone know how this could be possible?
This is a database on Windows Server 2016, SQL Server 12.0.4237.0
You create your FOREIGN KEY with NOCHECK, as a result the values that already exist in the table are not checked. This can be replicated with the following:
CREATE TABLE dbo.Address (ID int NOT NULL CONSTRAINT PK_Address PRIMARY KEY);
GO
CREATE TABLE dbo.Workplace (ID int NOT NULL CONSTRAINT PK_Workplace PRIMARY KEY,
AddressID int)
GO
INSERT INTO dbo.Workplace
VALUES(1,3); --Works
GO
ALTER TABLE dbo.WorkPlace WITH NOCHECK ADD CONSTRAINT FK_Workplace_Address FOREIGN KEY (AddressID) REFERENCES dbo.Address(ID);
GO
ALTER TABLE dbo.Workplace CHECK CONSTRAINT FK_Workplace_Address;
GO
INSERT INTO dbo.Workplace
VALUES(2,3); --Fails
GO
UPDATE dbo.Workplace
SET AddressID = 2
WHERE ID = 1; --Fails
GO
DROP TABLE dbo.Workplace;
DROP TABLE dbo.Address;
As you can see, only rows that are INSERTed or UPDATEd after the constraint was created (with NOCHECK) are validated; the first row INSERTed is left as it was, with a reference to a row that does not exist.
Instead, create the key without NOCHECK defined, or with CHECK, and the statement will fail when you try to create it:
CREATE TABLE dbo.Address (ID int NOT NULL CONSTRAINT PK_Address PRIMARY KEY);
GO
CREATE TABLE dbo.Workplace (ID int NOT NULL CONSTRAINT PK_Workplace PRIMARY KEY,
AddressID int)
GO
INSERT INTO dbo.Workplace
VALUES(1,3); --Works
GO
ALTER TABLE dbo.WorkPlace WITH CHECK ADD CONSTRAINT FK_Workplace_Address FOREIGN KEY (AddressID) REFERENCES dbo.Address(ID); --Fails
GO
DROP TABLE dbo.Workplace;
DROP TABLE dbo.Address;
This generates the error below:
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_Workplace_Address". The conflict occurred in database "Sandbox", table "dbo.Address", column 'ID'.
Alternatively, create the key when you create the tables; though it's likely too late for that now.
CREATE TABLE dbo.Address (ID int NOT NULL CONSTRAINT PK_Address PRIMARY KEY);
GO
CREATE TABLE dbo.Workplace (ID int NOT NULL CONSTRAINT PK_Workplace PRIMARY KEY,
AddressID int CONSTRAINT FK_Workplace_Address FOREIGN KEY REFERENCES dbo.Address(ID));
GO
INSERT INTO dbo.Workplace
VALUES(1,3); --Fails
GO
DROP TABLE dbo.Workplace;
DROP TABLE dbo.Address;

DB2 - Can I recreate a foreign key constraint in a table with data?

I'm having the following problem with DB2:
I need to change a column name (column "A"), but the thing is that column A has a PK constraint and a FK constraint. So I need to drop this constraints first, change the column name and then create the constraints again. But I remember a Professor told me once that you can't create a foreign key in a column which already has values. Is that true?
This is my script:
ALTER TABLE TARGET_TABLE
DROP PRIMARY KEY PK_A CASCADE;
ALTER TABLE TARGET_TABLE
RENAME COLUMN A TO B;
alter table TARGET_TABLE
add CONSTRAINT PK_B PRIMARY KEY( B);
alter table TARGET_TABLE add CONSTRAINT FK_B FOREIGN KEY( B) REFERENCES OTHER_TABLE(C);
Thanks in advance.
You can create a foreign key on a column which already has values. The only restriction is that the values must be be valid for the FK you are defining.
If not you will get an error such as
SQL0667N The FOREIGN KEY "I..." cannot be created because the table contains
rows with foreign key values that cannot be found in the parent key of the
parent table. SQLSTATE=23520

Alter Foreign Key Constraint Primary Key Error

I'm trying to drop and recreate a foreign key constraint, but I get an error
There are no primary or candidate keys in the referenced table 'inventory' that match the referencing column list in the foreign key 'fkInventory_VendorsInventory'.
I have already gone into the table design for both tables referenced in the code, and ensured that the column being referenced is a primary key.
ALTER TABLE inventory_vendors
DROP CONSTRAINT fkInventory_VendorsInventory;
ALTER TABLE inventory_vendors
ADD CONSTRAINT fkInventory_VendorsInventory
FOREIGN KEY(itemnum) REFERENCES inventory(itemnum)
ON UPDATE CASCADE
ON DELETE CASCADE
I have done such a drop and recreation before with no problems at all with another set of tables (unfortunately i don't remember which tables they were).
As you mentioned in comments, you have 2 primary key columns in the Inventory table:
one is itemnum, the other is store_id
I prepare a sample SQL here: 2 tables created
CREATE TABLE inventory
(
itemnum INT,
store_id INT,
inventoryDesc char(200),
primary key (itemnum, store_id)
);
CREATE TABLE inventory_vendors
(
inventory_vendors int,
itemnum INT,
store_id INT,
VendorDetails varchar(200),
primary key (inventory_vendors)
);
Create Unique constraint for one of the primary key. Here I am creating UNIQUE constraint for itemnum column
ALTER TABLE inventory
ADD CONSTRAINT [IX_inventory] UNIQUE ( [itemnum] )
GO
Then execute your script for creating the foreign key constraint on inventory_vendors for itemnum column and you can drop them as well.
ALTER TABLE inventory_vendors
ADD CONSTRAINT fk_Inventory_Vendors_Inventory
FOREIGN KEY(itemnum) REFERENCES inventory(itemnum)
ON UPDATE CASCADE
ON DELETE CASCADE
ALTER TABLE inventory_vendors
DROP CONSTRAINT fk_Inventory_Vendors_Inventory;
Hope this might help you..

How do I change a column I created a while ago in sql to foreign key

I need to change multiple tables to foreign keys. I have used the command
ALTER TABLE financial_transactions
ADD FOREIGN KEY (item_rental_id) REFERENCES transaction_id(item_rental_id);
The table name was financial_transactions and the column name was item_rental_id. It gives me an error saying:
Foreign key 'FK__financial__item___46E78A0C' references invalid table 'transaction_id'.
How do I resolve this?
Yes, this is a correct script to create a foreign key
ALTER TABLE financial_transactions
ADD FOREIGN KEY (item_rental_id) REFERENCES customer_rentals(item_rental_id);
However, if you are trying to add a foreign key constraint on a table with data already in it, you have to make sure that the item_rental_id in financial_transactions table are also in the customer_rentals table. Otherwise, you will have a referential integrity error.
To illustrate:
CREATE TABLE customer_rentals_1
(item_rental_id INT,
PRIMARY KEY (item_rental_id)
);
CREATE TABLE financial_transactions_1
(transaction_id INT,
item_rental_id INT,
PRIMARY KEY (transaction_id)
);
ALTER TABLE financial_transactions_1
ADD FOREIGN KEY (item_rental_id) REFERENCES customer_rentals_1(item_rental_id);
The scripts above will run successfully.
CREATE TABLE customer_rentals_2
(item_rental_id INT,
PRIMARY KEY (item_rental_id)
);
CREATE TABLE financial_transactions_2
(transaction_id INT,
item_rental_id INT,
PRIMARY KEY (transaction_id)
);
INSERT INTO financial_transactions_2
VALUES (1000, 9999);
ALTER TABLE financial_transactions_2
ADD FOREIGN KEY (item_rental_id) REFERENCES customer_rentals_2(item_rental_id);
But, this will have the following error since item_rental_id 9999 is not present in customer_rentals_2 table
Msg 547, Level 16, State 0, Line 31
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK__financial__item___76969D2E". The conflict occurred in database "TESTDB", table "dbo.customer_rentals_2", column 'item_rental_id'.
You have to give second table name
both way can do it
1. Add reference without name
ALTER TABLE financial_transactions
ADD FOREIGN KEY (item_rental_id) REFERENCES customer_rentals(item_rental_id);
2.Add reference with a name
ALTER TABLE financial_transactions
ADD CONSTRAINT FK_Financial_Transactions_Customer_Rental_Item_Rental_ID
FOREIGN KEY (item_rental_id) REFERENCES Customer_rentals(item_rental_id);

Foreign key constraint violation when foreign key is present

I am new to PostgreSQL and databases in general and am trying to figure out why I am getting a foreign key violation even when the foreign key is present.
The query I ran
insert into analytics_url_redirect
(source, news_item_id, access_date)
select 'n',id,CURRENT_TIMESTAMP from entry_entry_master
where id=43068778;
This fails with
ERROR: insert or update on table "analytics_url_redirect" violates foreign key constraint "news_item_id_refs_id_15ddd78c"
SQL state: 23503
Detail: Key (news_item_id)=(43068778) is not present in table "entry_entry_master".
The following select query also works fine and gives back 43068778:
select id from entry_entry_master where id=43068778;
The entire create table command - which is the output of django sqlall is
BEGIN;
CREATE TABLE "analytics_url_redirect" (
"id" serial NOT NULL PRIMARY KEY,
"newsletter_id" integer REFERENCES "nlc_newsletter" ("newslettercore_ptr_id") DEFERRABLE INITIALLY DEFERRED,
"alert_id" integer REFERENCES "alerts_emailtracker" ("id") DEFERRABLE INITIALLY DEFERRED,
"source" varchar(1) NOT NULL,
"brief_id" integer REFERENCES "brief_brief" ("id") DEFERRABLE INITIALLY DEFERRED,
"news_item_id" integer REFERENCES "entry_entry_master" ("id") DEFERRABLE INITIALLY DEFERRED,
"external_news_item_id" integer REFERENCES "nlc_newsletterblock" ("id") DEFERRABLE INITIALLY DEFERRED,
"recipient_id" integer REFERENCES "subscriber_subscriber" ("id") DEFERRABLE INITIALLY DEFERRED,
"external_recipient_id" integer REFERENCES "subscriber_pseudosubscriber" ("id") DEFERRABLE INITIALLY DEFERRED,
"access_date" timestamp with time zone NOT NULL
)
;
CREATE INDEX "analytics_url_redirect_newsletter_id" ON "analytics_url_redirect" ("newsletter_id");
CREATE INDEX "analytics_url_redirect_alert_id" ON "analytics_url_redirect" ("alert_id");
CREATE INDEX "analytics_url_redirect_brief_id" ON "analytics_url_redirect" ("brief_id");
CREATE INDEX "analytics_url_redirect_news_item_id" ON "analytics_url_redirect" ("news_item_id");
CREATE INDEX "analytics_url_redirect_external_news_item_id" ON "analytics_url_redirect" ("external_news_item_id");
CREATE INDEX "analytics_url_redirect_recipient_id" ON "analytics_url_redirect" ("recipient_id");
CREATE INDEX "analytics_url_redirect_external_recipient_id" ON "analytics_url_redirect" ("external_recipient_id");
CREATE INDEX "analytics_url_redirect_access_date" ON "analytics_url_redirect" ("access_date");
COMMIT;
So how is it possible to get a foreign key constraint violation when the foreign key is present?
Am I missing something obvious?
I figured this out myself. :(
One detail I missed is that entry_entry_master is partitioned using table inheritance. From the postgres documentation: postgres Inheritance
A serious limitation of the inheritance feature is that indexes (including unique constraints) and foreign key constraints only apply to single tables, not to their inheritance children. This is true on both the referencing and referenced sides of a foreign key constraint.
This also explains why the select query works.