SQL JDBC: parent key not found BUT the parent values have already been inserted into the database - sql

I'm having a problem inserting mock data into the database
The table I'm trying to insert the values into is called 'purchased'
which has some foreign key values such as patient_id, pharmacy_id, drug_id.
The other 3 tables 'patient', 'pharmacy', 'drug' have already been added to the database successfully.
Patient table has a foreign key which is doctor_id. The 'doctor' table has also been added to the database.
Since I know that a foreign key patient_id in 'purchased' table depends upon another foreign key doctod_id, so I've done something like this
foreign key (patient_id, receipt_no) references patient(patient_id,doctor_id) ON DELETE CASCADE
Not sure what I've missed here, why does sqlplus complain that the parent key is not found? Just working for a database class project so I'm still learning.
Here's .sql code file
https://gist.github.com/mopkaloppt/de8fbf64c4d5711c90e2b389a72911ba
Any kind of help will be much appreciated.. I'm freaking out a bit now as I've been struggling with this for a while and it's due soon :(

This part of your FK constraint looks confused:
foreign key (patient_id, receipt_no) references patient(patient_id,doctor_id) ON DELETE CASCADE
^^^^^^^^^^ ^^^^^^^^^
You are getting the errors because there is no receipt_no in your purchased data that matches a doctor_id. This is perhaps unsurprising as the data are unrelated.
Having looked at the data in your patient table, it seems you haven't got the database design quite right. There's duplication in that table: if a patient has multiple illnesses or sees multiple doctors then there are repeated values for all other columns. This is also getting in the way of your FK constraint: you are trying to link a row in purchased to a patient, but there are multiple rows for some patients, so which row do you link to?
It seems you have a many-to-many relationship between patients and illnesses (a patient can have multiple illnesses, multiple patients can have an illness), and also between patients and doctors (a patient can be seen by multiple doctors, a doctor can see multiple patients). So, I would recommend introducing new tables for the relationships between patients and illnesses, and between patients and doctors. For example, here's the table you could use for the relationship between patients and doctors. Insert one row into this table for each combination of patient and doctor:
create table patient_doctor (
patient_id char(4) not null,
doctor_id char (4) not null,
primary key (patient_id, doctor_id),
foreign key (patient_id) references patient(patient_id) on delete cascade,
foreign key (doctor_id) references doctor(doctor_id) on delete cascade);
A linking table such as this is the standard way of representing a many-to-many connection in a relational database.
You don't yet have a table for illnesses, so I'll leave you to create a table for them, a linking table similar to that for patients and doctors (patient_illness perhaps), and the data in both tables.
Once you've done that, remove the doctor_id and illness columns from patient, remove the duplicate rows and make the patient PK depend only on patient. Your FK constraint from purchased to patient can then reference only patient_id.
Hopefully after doing all of this you should see your FK constraint violation errors go away.

Related

Query regarding SQLite and Cascading foreign keys

I am currently writing a application in C# that uses a SQLite database to store information the user will input. The application is basically a Management system for users who are called "Students" in the application. This is the most important table in my database and every other table is linked off this table. What I want to do is when a student is removed - they leave the institute/get kicked out etc. - is to remove their data from all the other tables so that data is no longer there as it isn't needed. An example of some of the Create table statements I have written is:
CREATE TABLE student(studentID int(5) PRIMARY KEY NOT NULL, name string(16),...,DOB string(8) );
CREATE TABLE emergencyContact(emergencyID int(5) PRIMARY KEY NOT NULL, name string(16),..., contactNumber int(16));
CREATE TABLE emergencyContactOf(studentID int(5) FOREIGN KEY REFERENCES student('studentID'), emergencyID int(5) FOREIGN KEY REFERENCES emergencyContact('emergencyID');
I have read up on this and my understanding is the data will be deleted in the EmergencyContactOf table if I include a 'ON DELETE CASCADE' statement as the StudentID key will no longer be present in the Parent table.
However, my understanding is the data in the EmergencyContact table that is for that specific student will not be deleted as there is no reference to the StudentID.
My question is, is there a way to remove the data from this table also that is relevant to that Student? For example, if I was to include a column in the EmergencyContact table which would reference the StudentID as a Foreign Key and then remove that row if the StudentID is ever deleted from the parent table? Is this a good solution to this particular problem?
All other tables I have are also designed in this way, where the data is in different tables and then linked back to the Student table with relationship tables so this will also apply to all the other tables I have.
Thanks.
My question is, is there a way to remove the data from this table also that is relevant to that Student? For example, if I was to include a column in the EmergencyContact table which would reference the StudentID as a Foreign Key and then remove that row if the StudentID is ever deleted from the parent table? Is this a good solution to this particular problem?
What happens if multiple students have the same emergency contact? You don't want to duplicate data if you don't have to - that's the whole point of the emergencyContactOf table, to efficiently set up a many to many relation between students and emergency contacts. So you don't want to do something like you describe.
You could periodically (Monthly, yearly, after purging student rosters, whatever) run a delete that removes rows from emergencyContact if they don't appear in emergencyContactOf:
DELETE FROM emergencyContact
WHERE emergencyID NOT IN (SELECT emergencyID FROM emergencyContactOf)
or the like.
Hmm, I see two scenarios here. What if two students have the same emergency contact, say two bothers having their father as emergency contact?
If in such a case you store only one record (the father) in the emergency contact table, you don't want to delete the emergency contact if only one of them leaves. You'd delete the emergency contact for the other one. So you'd need additional logic, when to delete an emergency contact. You could put that in a trigger.
You use a less sophisticated approach and multiple rows from the emergency contact table can map to one person in real life. In that case you can pull the reference to the student directly into the emergency contact table and use ON DELETE CASCADE there.
CREATE TABLE student
(studentid int(5),
name string(16),
...
PRIMARY KEY (studentid),
...);
...
CREATE TABLE emergencycontact
(emergencycontactid int(5),
studentid int(5),
name string(16),
...
PRIMARY KEY (emergencycontactid),
FOREIGN KEY (studentid)
REFERENCES student
(studentid),
...);
The second might be tempting but the "clean way" is the first one, as the second allows contradicting data. From what you posted you're already on the "clean way". But a mentioned that required triggers.

Insert into tables with primary and foreign key at same time

Very new to SQL and have spent a day on this already.
Here are my two tables:
Centre(cid, name, location, nurse_supervisor)
Nurse(nid, name, centre_id, certificate)
I have a big problem. The (nurse_supervisor) in Centre is a foreign key to Nurse (nid).
The (centre_id) in Nurse is a foreign key to (Centre cid).
I can't figure out how to populate these tables. I have tried:
INSERT ALL, which produces "A foreign key value has no matching primary key value"
I have tried removing the foreign key constraints and adding them after populating the tables but when I do that it says I can't add a constraint to tables with preexisting data.
I tried removing NOT NULL - but realized that was silly as the constraints will be enforced anyways.
Everything I look through says populate the parent table first and then the child, but these tables are linked to each other.
I am using SQL developer.
This is a poor schema design, but one way to get around it would be to:
Make both centre_id and nurse_supervisor columns NULL in the two table definitions
Insert all rows into both tables, but with NULL for those two columns
Update centre_id to the correct value for each row in the Nurse table
Update nurse_supervisor to the correct value for each row in the Centre table

Delete entry in table that is referenced by another table

I have two tables, Games and Sport_Games. Sport_Games has a foreign key that references Games.
Now when I want to delete an entry from Games I get an error saying:
"FK__Sport_Gam__game___1D7C2B7C". The conflict occurred in database "DatabasesProject", table "dbo.Sport_Games", column 'game_id'.
The statement has been terminated.
Im assuming this is because I can't delete something from a table with a constraint without dropping it. I tried adding ON DELETE CASCADE in table Games in hopes that when I delete a game it is also deleted from Sport_Games but that didn't work either.
Here are the tables for reference:
CREATE TABLE Games(
game_id INT IDENTITY PRIMARY KEY,
name VARCHAR(50),
release_date date,
rating INT,
min_age INT,
development_team_email VARCHAR(50) FOREIGN KEY REFERENCES Development_Teams,
release_conference INT FOREIGN KEY REFERENCES Conferences
--ON DELETE CASCADE ---this is what I added
)
CREATE TABLE Sport_Games(
game_id INT PRIMARY KEY FOREIGN KEY REFERENCES Games,
sport_type VARCHAR(50),
)
I have other tables referencing Games as well, action_games, strategy_games, But they're similar to sport_games
If you know the GameID of the records you are deleting, simply delete records in the Sport_Games table that have the same GameID first, then delete from Games table.
For the cascade to work, you need to ad that to the FK definition on the Sport_Games table. That way when the Games record is deleted, the sport_Games record will be deleted as well.
Your ON DELETE CASCADE will work for you if you put it on the correct Foreign Key. But to answer your question...
Since Games.Game_ID has a constraining reference against Sport_Games.Game_ID, you need to either turn the constraint off so it will not prevent you from deleting, but this will leave orphans, or delete the children before you delete the parent. Since you must already know the PK value to delete the parent, you simply:
DELETE FROM Sport_Games WHERE Game_ID = [The id you are using to delete from Games]
Then you delete the parent:
DELETE FROM Games WHERE Game_ID = [Same id used above]
What I'm about to say might not directly answer your question, but it could be beneficial for you moving forward or in future designs.
I've found placing "delete_date" and "delete_id" in tables to be a useful way to work around these FK constraint issues. If your application is only calling existing stored procedures and table functions you could update the queries to include "AND delete_date IS NULL" in their conditions. If your application is writing adhoc queries, it might also be as simple as creating or modifying a base class to inject the condition that omits soft-deleted rows.

MS SQL creating many-to-many relation with a junction table

I'm using Microsoft SQL Server Management Studio and while creating a junction table should I create an ID column for the junction table, if so should I also make it the primary key and identity column? Or just keep 2 columns for the tables I'm joining in the many-to-many relation?
For example if this would be the many-to many tables:
MOVIE
Movie_ID
Name
etc...
CATEGORY
Category_ID
Name
etc...
Should I make the junction table:
MOVIE_CATEGORY_JUNCTION
Movie_ID
Category_ID
Movie_Category_Junction_ID
[and make the Movie_Category_Junction_ID my Primary Key and use it as the Identity Column] ?
Or:
MOVIE_CATEGORY_JUNCTION
Movie_ID
Category_ID
[and just leave it at that with no primary key or identity table] ?
I would use the second junction table:
MOVIE_CATEGORY_JUNCTION
Movie_ID
Category_ID
The primary key would be the combination of both columns. You would also have a foreign key from each column to the Movie and Category table.
The junction table would look similar to this:
create table movie_category_junction
(
movie_id int,
category_id int,
CONSTRAINT movie_cat_pk PRIMARY KEY (movie_id, category_id),
CONSTRAINT FK_movie
FOREIGN KEY (movie_id) REFERENCES movie (movie_id),
CONSTRAINT FK_category
FOREIGN KEY (category_id) REFERENCES category (category_id)
);
See SQL Fiddle with Demo.
Using these two fields as the PRIMARY KEY will prevent duplicate movie/category combinations from being added to the table.
There are different schools of thought on this. One school prefers including a primary key and naming the linking table something more significant than just the two tables it is linking. The reasoning is that although the table may start out seeming like just a linking table, it may become its own table with significant data.
An example is a many-to-many between magazines and subscribers. Really that link is a subscription with its own attributes, like expiration date, payment status, etc.
However, I think sometimes a linking table is just a linking table. The many to many relationship with categories is a good example of this.
So in this case, a separate one field primary key is not necessary. You could have a auto-assign key, which wouldn't hurt anything, and would make deleting specific records easier. It might be good as a general practice, so if the table later develops into a significant table with its own significant data (as subscriptions) it will already have an auto-assign primary key.
You can put a unique index on the two fields to avoid duplicates. This will even prevent duplicates if you have a separate auto-assign key. You could use both fields as your primary key (which is also a unique index).
So, the one school of thought can stick with integer auto-assign primary keys, and avoids compound primary keys. This is not the only way to do it, and maybe not the best, but it won't lead you wrong, into a problem where you really regret it.
But, for something like what you are doing, you will probably be fine with just the two fields. I'd still recommend either making the two fields a compound primary key, or at least putting a unique index on the two fields.
I would go with the 2nd junction table. But make those two fields as Primary key. That will restrict duplicate entries.

Data Modeling: What is a good relational design when a table has several foreign key constrainst to a single table?

I have 2 tables:
1. Employees
2. Vouchers
Employees table has a single primary key.
Vouchers table has 3 foreign key constraints referencing the Employees table.
The following is a sample T-SQL script (not the actual table script) to create both tables and their relationship in SQL Server:
IF OBJECT_ID('dbo.Vouchers') IS NOT NULL
DROP TABLE dbo.Vouchers
IF OBJECT_ID('dbo.Employees') IS NOT NULL
DROP TABLE dbo.Employees
GO
CREATE TABLE Employees
(
ObjectID INT NOT NULL PRIMARY KEY IDENTITY
)
CREATE TABLE Vouchers
(
ObjectID INT NOT NULL PRIMARY KEY IDENTITY,
IssuedBy INT,
ReceivedBy INT,
ApprovedBy INT,
CONSTRAINT fk_Vouchers_Employees_IssuedBy FOREIGN KEY (IssuedBy)
REFERENCES Employees (ObjectID)
ON UPDATE CASCADE
ON DELETE NO ACTION,
CONSTRAINT fk_Vouchers_Employees_ReceivedBy FOREIGN KEY (ReceivedBy)
REFERENCES Employees (ObjectID)
ON UPDATE CASCADE
ON DELETE NO ACTION,
CONSTRAINT fk_Vouchers_Employees_ApprovedBy FOREIGN KEY (ApprovedBy)
REFERENCES Employees (ObjectID)
ON UPDATE CASCADE
ON DELETE NO ACTION
)
But an error is thrown:
Msg 1785, Level 16, State 0, Line 7
Introducing FOREIGN KEY constraint 'fk_Vouchers_Employees_ReceivedBy' on table 'Vouchers' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I don't have an idea of what efficient solution is available here. The requirements on the relationship is that: whenever an Employee is deleted, the Voucher that references some of its columns to the Employee does not get deleted (ON DELETE CASCADE is not an option). Instead, the values of the columns (IssuedBy, ReceivedBy and/or ApprovedBy) that are referenced to the deleted Employee should be set to NULL (since the columns are NULLABLE).
Many thanks!
Strictly from a relational design point of view, the Vouchers table as three Foreign Keys. Whether you choose to enforce them, through CASCADE assertions or otherwise, is an implementation issue, but the relational design still exists. Presumably you want to enforce that, if one of the three fields is not NULL, then a matching record needs to exist. Or not. It's an implementation issue as to whether or not you care to enforce the design.
However, the violations you describe are assumed at your peril. The fact that you're asking this question suggests you may not fully appreciate all the ways these choices can lead into the swamp.
I think the error may be a consequence of the fact that more than one of the three might refer to the same employee.
BTW, I've in very few cases ever found it necessary to delete records in such a fashion that CASCADES are useful. Usually that would be used to prevent the database from being too big; and database capacity is less and less an issue over time.
I would not actually delete Employees, but instead use a trigger to set a flag to mark them as deleted.
I generally don't turn on cascade of Updates or Deletes, but instead require an application to explicitly perform these actions.
From a design standpoint it seems good to have the 3 foreign keys you listed. It looks like the error message you are getting relates to the ON UPDATE CASCADE options on your foreign keys (although I was able to create the table as specified). Regardless, to get the behavior you mention wanting, I would recommend a trigger on the Employees table, that fires before you delete the record. This trigger would find instances of the Employees.OjbectID in the Vouchers table and set them to NULL.