PostgreSQL Foreign Key and Child Tables - sql

I have the following tables:
--Competition tables
CREATE TABLE IF NOT EXISTS Tr.Competitions(
competition_id SERIAL PRIMARY KEY,
competition_name text NOT NULL
);
CREATE TABLE IF NOT EXISTS Tr.CompetitionsQuestions(
competition_id int NOT NULL,
question_id int NOT NULL,
FOREIGN KEY (competition_id) REFERENCES Tr.Competitions(competition_id),
FOREIGN KEY (question_id) REFERENCES Tr.Questions(question_id)
);
--Questions tables
CREATE TABLE IF NOT EXISTS Tr.Questions(
question_id SERIAL PRIMARY KEY,
question_text text NOT NULL
);
CREATE TABLE IF NOT EXISTS Tr.MultiQuestions(
possible_answers text ARRAY NOT NULL,
correct_answer int NOT NULL
) INHERITS(Tr.Questions);
I try to insert some dummy data into Tr.CompetitionQuestions like so:
--Test Fixtures
INSERT INTO Tr.MultiQuestions (question_text, possible_answers, correct_answer)
VALUES ('Which of the following is awesome?', '{"Indian Food","Soccer","All the above"}', 2);
INSERT INTO Tr.Competitions(competition_name)
VALUES ('Awesome Competition');
INSERT INTO Tr.CompetitionsQuestions(competition_id, question_id)
VALUES ((SELECT competition_id FROM Tr.Competitions WHERE competition_id=1),
(SELECT question_id FROM Tr.Questions WHERE question_id=1));
Having these stored in an .sql file and running \i some.sql is gerenating the following error. How do I add a question foreign key to the CompetitionsQuestions table?
ERROR: insert or update on table "competitionsquestions" violates foreign key constraint "competitionsquestions_question_id_fkey"
DETAIL: Key (question_id)=(1) is not present in table "questions".
Seems like a weird error since SELECT * FROM tr.questions WHERE question_id=1 actually gives me the stored multiquestion row.
EDIT:
Simplifying to:
INSERT INTO Tr.CompetitionsQuestions(competition_id, question_id)
VALUES (1, 1);
gives me the same error;

(Assuming, from comments, that you're using PostgreSQL's table inheritance features, since your question doesn't really contain full info about the schema and how you populate its contents):
Foreign keys don't apply across all members of an inheritance tree. They can only be to the specific table.
The same is true of a UNIQUE constraint or PRIMARY KEY.
You can see what a foreign key constraint will see in a table if you:
SELECT * FROM ONLY thetable;
The ONLY keyword tells PostgreSQL not to include child tables. That's what's used in foreign key constraint checks.

Related

Many-to-Many Link Table Foreign Key Modeling in SQLite

I have the following two tables in SQLite:
CREATE TABLE `Link` (
`link_id` integer NOT NULL,
`part_id` integer NOT NULL,
CONSTRAINT `link_pk` PRIMARY KEY(`link_id`,`part_id`)
);
CREATE TABLE `Main` (
`main_id` integer NOT NULL PRIMARY KEY AUTOINCREMENT,
`link_id` integer NOT NULL REFERENCES `Link`(`link_id`)
);
INSERT INTO `Link` (link_id, part_id) VALUES (1,10);
INSERT INTO `Link` (link_id, part_id) VALUES (1,11);
INSERT INTO `Link` (link_id, part_id) VALUES (1,12);
INSERT INTO `Link` (link_id, part_id) VALUES (2,15);
INSERT INTO `Main` (main_id, link_id) VALUES (1,1);
INSERT INTO `Main` (main_id, link_id) VALUES (2,1);
INSERT INTO `Main` (main_id, link_id) VALUES (3,2);
Many Main rows may reference the same link id, and many Link rows may have the same link id, such that select * from Main natural join Link where main_id=1 will return N rows, and select * from Main where link_id=1 will return K rows. The link id is important, and the original data each main has 1 link id, and each link has N part ids.
Using the schemas above, I am unable to insert any rows in Main due to the foreign key constraint (foreign key mismatch - "Main" referencing "Link": INSERT INTO Main (main_id, link_id) VALUES (1,1);), presumably because of the composite key requirement. I can get this to work by removing the foreign key constraint, but then I am obviously missing a constraint. Reversing the direction of the key wouldn't work either since, as stated above, it's a Many-to-Many relationship. Is there a way to properly model this in SQLite with a constraint that at least one row exists in Link for each link_id in Main?
I would propose a different design.
Each of the 2 entities link_id and part_id should be the primary key in 2 tables, something like:
CREATE TABLE Links (
link_id INTEGER PRIMARY KEY,
link_description TEXT
);
CREATE TABLE Parts (
part_id INTEGER PRIMARY KEY,
part_description TEXT
);
Then, create the junction table of the above tables (like your current Link table):
CREATE TABLE Links_Parts (
link_id INTEGER NOT NULL REFERENCES Links(link_id),
part_id INTEGER NOT NULL REFERENCES Parts(part_id),
PRIMARY KEY(link_id, part_id)
);
and the table Main:
CREATE TABLE Main (
main_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
link_id INTEGER NOT NULL REFERENCES Links(link_id)
);
All the relations are there and you have referential integrity guaranteed if you set foreign key support:
PRAGMA foreign_keys = ON;
See a simplified demo.

Check if data exists in another table on insert?

Table A
(
Table_A_ID int
)
Table B
(
Table_B_ID int
Value int
)
Say I want to insert data into Table B, where 'Value' would be the same as a Table_A_ID.
How would I make a constraint or check that the data actually exists in the table on insertion?
You probably need to enforce data integrity not only on INSERT into Table B, but also on UPDATE and DELETE in both tables.
Anyway options are:
FOREIGN KEY CONSTRAINT on Table B
TRIGGERs on both tables
As a last resort if for some reason 1 and 2 is not an option STORED PROCEDUREs for all insert, delete update operations for both tables
The preferred way to go in most cases is FOREIGN KEY CONSTRAINT.
Yap, I agree with #peterm.
Cause, if your both Table_A_ID and Table_B_Id are primary keys for both tables, then you don't even need two tables to store the value. Since, your two tables are seems to be on 'one-to-one' relationship. It's one of the database integrity issues.
I think you didn't do proper normalisation for this database.
Just suggesting a good idea!
I found this example which demonstrates how to setup a foreign key constraint.
Create employee table
CREATE TABLE employee (
id smallint(5) unsigned NOT NULL,
firstname varchar(30),
lastname varchar(30),
birthdate date,
PRIMARY KEY (id),
KEY idx_lastname (lastname)
) ENGINE=InnoDB;
Create borrowed table
CREATE TABLE borrowed (
ref int(10) unsigned NOT NULL auto_increment,
employeeid smallint(5) unsigned NOT NULL,
book varchar(50),
PRIMARY KEY (ref)
) ENGINE=InnoDB;
Add a constraint to borrowed table
ALTER TABLE borrowed
ADD CONSTRAINT FK_borrowed
FOREIGN KEY (employeeid) REFERENCES employee(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
NOTE: This tells MySQL that we want to alter the borrowed table by adding a constraint called ‘FK_borrowed’. The employeeid column will reference the id column in the employee table – in other words, an employee must exist before they can borrow a book.
The final two lines are perhaps the most interesting. They state that if an employee ID is updated or an employee is deleted, the changes should be applied to the borrowed table.
NOTE: See the above URL for more details, this is just an excerpt from that article!
Create a foreign key constraint on the column 'Value' on table B that references the 'Table_A_ID' column.
Doing this will only allow values that exist in table A to be added into the 'Value' field of table B.
To accomplish this you first need to make Table_A_ID column the primary key for table A, or it at least has to have some sort of unique constraint applied to it to be a foreign key candidate.
BEGIN TRANSACTION -- REMOVE TRANSACTION AND ROLLBACK AFTER DONE TESTING
--PUT A PRIMARY KEY ON TABLE A
CREATE TABLE A
( Table_A_ID int CONSTRAINT PK_A_Table_A_ID PRIMARY KEY)
--ON VALUE ADD A FOREIGN KEY CONSTRAINT THAT REFERENCEs TABLE A
CREATE TABLE B
( Table_B_ID int,
[Value] int CONSTRAINT FK_B_Value_A REFERENCES A(Table_A_ID)
)
-- TEST VALID INSERT
INSERT A (Table_A_ID) VALUES (1)
INSERT B (Table_B_ID, [Value]) VALUES (1,1)
--NOT ALLOW TO INSERT A VALUE THAT DOES NOT EXIST IN A
--THIS WILL THROW A FOREIGN KEY CONSTRAINT ERROR
INSERT B (Table_B_ID, [Value]) VALUES (1,2) -- 2 DNE in table A
ROLLBACK
Note: there is no magic to 'FK_B_Value_A' or 'PK_A_Table_A_ID' it simply a naming convention and be called anything. The syntax on the foreign key and primary key lines work like this:
column-definition CONSTRAINT give-the-constraint-a-name REFERENCES table-name ( table-column )
column-definition CONSTRAINT give-the-constraint-a-name PRIMARY KEY

Problem creating foreign keys in mySql

I created a foreign key in my sql by the following statemnt..
ALTER TABLE `users` ADD FOREIGN KEY ( `id`)
REFERENCES `user_login` (`user_id`)
ON DELETE CASCADE ;
The creation appears to succeed then after that I execute a delete statement
DELETE From user_login WHERE user_id = 1576;
yet in users the row still exists that is referencing that. I open up the mysql workbench and it doesn't show any signs that the foreign key was created. Does anyone know why this would be? Or what I am doing wrong? It is a one-to-one relationship in the two tables.
The table may be in MyISAM format, which does not support foreign keys.
Try converting it to InnoDB first:
alter table users engine=InnoDB;
You have to also make sure that both users.id and user_login.user_id have an index each.
Copy and paste this code in your Mysql script editor and run. You will have two tables categories and products these tables having cat_id as foreign key.
CREATE DATABASE IF NOT EXISTS dbdemo;
USE dbdemo;
CREATE TABLE categories(
cat_id int not null auto_increment primary key,
cat_name varchar(255) not null,
cat_description text
) ENGINE=InnoDB;
CREATE TABLE products(
prd_id int not null auto_increment primary key,
prd_name varchar(355) not null,
prd_price decimal,
cat_id int not null,
FOREIGN KEY fk_cat(cat_id)
REFERENCES categories(cat_id)
ON UPDATE CASCADE
ON DELETE RESTRICT
)ENGINE=InnoDB;

Foreign Key doesn't stop me entering data MySQL

I have a table which has one column as a foreign key joining to another table.
It's a cricket question where I have a table called Fixtures and another called Inning.
Inning table has a FixtureId column relates to the Fixture table.
I would expect that If i do a insert on the inning table using a FixtureId that doesn't relate to a Fixture then it would error but this isn't the case...
Can anyone explain why this is?
Make sure that you are using the InnoDB storage engine when creating the table. Other storage engines will simply ignore foreign key constraints. (Source)
Example:
CREATE TABLE a (
id INT AUTO_INCREMENT PRIMARY KEY
) ENGINE=INNODB;
CREATE TABLE b (
id INT AUTO_INCREMENT PRIMARY KEY,
a_id INT,
FOREIGN KEY (a_id) REFERENCES a(id)
) ENGINE=INNODB;
INSERT INTO b (id, a_id) VALUES(NULL, 1);
The above insert fails with:
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails...
im not sure i understand can you elaborate and show your sql statements?
if i understand correctly, ( which i may not ) your foreign key would just be blank if you did not have a value for that field, why would it error out?

Suggestions for insert with Foreign key constraint in mysql

I have a table containing 2 entries.
Something like
CREATE TABLE `db`.`main` (
`id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
);
The id for these 2 entries are automatically generated primary keys.
I have another table with a rule linking
CREATE TABLE `db`.`day` (
`main_id` int(10) unsigned NOT NULL,
`day` tinyint(4) NOT NULL,
CONSTRAINT `fk_db_main` FOREIGN KEY (`main_id`) REFERENCES `main` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
);
now I can successfully get a result using
SELECT * FROM main where id='9';
but when I try to run
INSERT INTO day (main_id, day) VALUES (9, 0);
I get
"Cannot add or update a child row: a foreign key constraint fails (db.day, CONSTRAINT fk_db_main FOREIGN KEY (main_id) REFERENCES main (id) ON DELETE NO ACTION ON UPDATE NO ACTION) (1452)"
Any suggestions on what I am missing with the insert?
**I hadn't listed the actual cause of the issue while asking the question. The actual cause was that the main db table was in MyISAM, and the InnoDB tables couldn't create a foreign key connecting to it. In short, MyISAM doesn't support foreign keys, even when they are coming from other tables.
The insert works for me if I remove the db. parts in the CREATE TABLE statements (and insert into main a row with an id of 9). Maybe the problem is that you're using that db. prefix inconsistently, i.e. after TABLE but not in the CONSTRAINT clause...?
The FOREIGN KEY constraint says "there shall be an entry in the 'main` table with an ID value that matches the newly inserted 'main_id' value in the 'day' table".
When you INSERT the value 9 into 'day', is there already a row in 'main' with ID = 9?
The DBMS doesn't think so - that's why it complained.
I hadn't listed the actual cause of the issue while asking the question. The actual cause was that the main db table was in MyISAM, and the InnoDB tables couldn't create a foreign key connecting to it. In short, MyISAM doesn't support foreign keys, even when they are coming from other tables.