Foreign Key Constraint Incorrectly Formed - mysql

I'm having an issue in which I can't create some tables because their foreign keys are "incorrectly formed." I don't know why I'm getting this error because the keys are of the same type and have unique names. The code is pretty straightforward:
CREATE TABLE Vehicle(
vin VARCHAR(25),
ID VARCHAR(20),
make VARCHAR(20),
model VARCHAR(20),
year_ VARCHAR(20),
condition_ VARCHAR(20),
PRIMARY KEY(vin, ID),
FOREIGN KEY(ID) REFERENCES Listing(CarID)
);
CREATE TABLE Listing(
CarID VARCHAR(20),
state_ VARCHAR(20),
price INT(10),
url VARCHAR(50),
PRIMARY KEY(CarID),
FOREIGN KEY(CarID) REFERENCES Vehicle(ID)
);

If the primary key of Vehicle is (vin, ID) then a foreign key that references it must also have two columns, in the same order, with the same data types.
But in your case, your Listing table doesn't have a vin column, so it can't reference the primary key of Vehicle.
What would it mean for Listing to reference part of the primary key of Vehicle? There is no guarantee that only one row exists with a given value of ID in Vehicle. It could have multiple rows with the same ID and different vin values. So a Listing could potentially be the child of multiple vehicles, which probably doesn't make sense.
So you must either:
Add a vin column to Listing, so its foreign key can reference exactly one row by its primary key (both columns).
Modify the Vehicle table to make ID alone be its primary key.
Update:
I just noticed that you appear to have foreign keys in both tables. That's not usually needed. As I think about what you are trying to model, I would guess that a Listing has one or more Vehicles, right? So the Vehicle should reference its parent Listing. But a Listing does not need to reference a Vehicle.
So the following works:
First, create the parent table, because you can't make a foreign key until the parent table exists. In your case, the Listing is the parent table.
CREATE TABLE Listing(
CarID VARCHAR(20),
state_ VARCHAR(20),
price INT(10),
url VARCHAR(50),
PRIMARY KEY(CarID)
);
Then create the child table, with a foreign key that references its parent.
CREATE TABLE Vehicle(
vin VARCHAR(25),
ID VARCHAR(20),
make VARCHAR(20),
model VARCHAR(20),
year_ VARCHAR(20),
condition_ VARCHAR(20),
PRIMARY KEY(vin, ID),
FOREIGN KEY(ID) REFERENCES Listing(CarID)
);
I tested on MySQL 5.7.27.

This is the correct way:
CREATE TABLE Vehicle(
vin VARCHAR(25),
ID VARCHAR(20),
make VARCHAR(20),
model VARCHAR(20),
year_ VARCHAR(20),
condition_ VARCHAR(20),
PRIMARY KEY(vin, ID)
);
CREATE TABLE Listing(
CarVin VARCHAR(25),
CarID VARCHAR(20),
state_ VARCHAR(20),
price INT(10),
url VARCHAR(50),
PRIMARY KEY(CarVin, CarID),
FOREIGN KEY(CarVin, CarID) REFERENCES Vehicle(vin, ID)
);
In your data model there are a listing and vehicles.
A vehicle can be in the Listing (or not). The Listing have n vehicles (so you have a 1 to n relation).
Each row in Listing have a reference to a vehicle, so it need the vehicle primary key fields (or some unique fields) to reference to it (The vehicle primary key is compound by "vin" and "ID" fields).
So the primary key in Listing should be compound by "vin" and "ID" too, like in Vehicle's table. It's a good practice to use integer autoincrement field as primary key for each table.
You need to add in table Listing "CarVin" field and then the foreign key in Listing will be FOREIGN KEY(CarVin, CarID) REFERENCES Vehicle(vin, ID).
The foreign key will check, when you insert a Listing row, that the vehicle with the values CarVin and CarID exist.
To add a foreign key in table Vehicle too, you should disable foreign key check (SET FOREIGN_KEY_CHECKS=0;) until the tables will be created, because table Listing doesn't exist yet (if you create first table Vehicle). But I think it doesn't have sense in this case, at least you want ensure that all Vehicles are in Listing table.
To insert data you have to insert first a vehicle, and then add it to Listing table (foreign key is check, at least you disable foreign key check until the end), otherwise the foreign key check will fail.
In #"Bill Karwin"'s model you need first insert the Listing row and then the Vehicle.
MySql foreign key constraint documentation, each storage engine in MySql implements foreign key constraint in different way.
Both tables with foreign key constraint to each other:
SET FOREIGN_KEY_CHECKS=0;
CREATE TABLE Vehicle(
vin VARCHAR(25),
ID VARCHAR(20),
make VARCHAR(20),
model VARCHAR(20),
year_ VARCHAR(20),
condition_ VARCHAR(20),
PRIMARY KEY(vin, ID),
FOREIGN KEY(vin, ID) REFERENCES Listing(CarVin, CarID)
);
CREATE TABLE Listing(
CarVin VARCHAR(25),
CarID VARCHAR(20),
state_ VARCHAR(20),
price INT(10),
url VARCHAR(50),
PRIMARY KEY(CarVin, CarID),
FOREIGN KEY(CarVin, CarID) REFERENCES Vehicle(vin, ID)
);
SET FOREIGN_KEY_CHECKS = 1;

Related

SQL many to many relationship table

I'm trying to make a many-to-many relationship between these two tables.
CREATE TABLE series (
title VARCHAR(30),
language VARCHAR(30),
year INT,
PRIMARY KEY (title)
);
CREATE TABLE actors (
name VARCHAR(30),
age INT,
country VARCHAR(30),
serie VARCHAR(30),
PRIMARY KEY (name , age , country , serie),
FOREIGN KEY (serie)
REFERENCES series (title)
);
I tried to create a separate table to form a many-to-many relation. Does this look correct?
CREATE TABLE series_actors_combined (
title VARCHAR(30),
name VARCHAR(30),
age INT,
country VARCHAR(30),
serie VARCHAR(30),
FOREIGN KEY (title)
REFERENCES series (title),
FOREIGN KEY (name)
REFERENCES actors (name),
FOREIGN KEY (age)
REFERENCES actors (age),
FOREIGN KEY (country)
REFERENCES actors (country),
FOREIGN KEY (serie)
REFERENCES actors (serie)
);
Your tables do not look right.
As a starter, you have a foreign key in the actors table that references the series, which basically defeats the purpose of the bridge table.
Also, the foreign keys from the bridge table to actors are not well-formed: you have one key per column, while you should have a unique, multi-column key to reference the actor. I would recomend having auto incremented primary keys rather than using these combinations of columns. This gives more flexibility to your design (in real life, two different series might have the same title), and makes it much easier to create foreign keys in the bridge table.
Consider:
create table series(
id int primary key auto_increment,
title varchar(30) unique,
language varchar(30),
year int
);
create table actors(
id int primary key auto_increment,
name varchar(30),
dob date, -- better than age
country varchar(30)
);
create table series_actors(
series_id int references series(id),
actor_id int references actors(id),
primary key (series_id, actor_id)
)

How to implement an Aggregation in SQL? (This is not about GroupBy)

In the scope of a university project I am supposed to implement an aggregation of my database.
I'm given a Entity-Relationship model which looks similar to this one:
Now I'm supposed to implement a SQL-Script which creates a database like this, but I cannot find anything about this topic on google or anywhere else. In the slides of my Professor it says
For example, to represent aggregation manages between relationship works_on and entity set manager, create a schema
manages(employee_id, branch_name, title,manager_name)
Schema works_on is redundant provided we are willing to store null values for attribute manager_name in relation on schema manages
So I tried to just put two tables into my SQL-Script one called works-on and one called manages. In works-on I put all the Primary keys of job, branch and employee and defined them as foreign keys. In manages I put all of these Primary keys and additionally I put manager. Now the problem is that when I use the Reverse-Engineer of MySQL-workbench to create the EER-Model of the database, I don't get anything out of it which has to do with this aggregation. So what am I doing wrong here?
As requested by #Barmar I just wrote the CREATE TABLE-statements that I would've used for this:
CREATE TABLE job
(jobid INT,
PRIMARY KEY(jobid));
CREATE TABLE employee
(employeeid INT,
PRIMARY KEY(employeeid));
CREATE TABLE branch
(branchid INT,
PRIMARY KEY(branchid));
CREATE TABLE manager
(managerid INT,
PRIMARY KEY(managerid));
CREATE TABLE works_on
(jobid INT, KEY(jobid),
branchid INT, KEY(branchid),
employeeid INT, KEY(employeeid));
CREATE TABLE manages
(jobid INT, KEY(jobid),
branchid INT, KEY(branchid),
employeeid INT, KEY(employeeid),
managerid INT, KEY(managerid));
ALTER TABLE works_on
ADD CONSTRAINT FK_workson_employee FOREIGN KEY(employeeid) REFERENCES employee(employeeid);
ALTER TABLE works_on
ADD CONSTRAINT FK_workson_branch FOREIGN KEY(branchid) REFERENCES branch(branchid);
ALTER TABLE works_on
ADD CONSTRAINT FK_workson_job FOREIGN KEY(jobid) REFERENCES job(jobid);
ALTER TABLE manages
ADD CONSTRAINT FK_manages_employee FOREIGN KEY(employeeid) REFERENCES employee(employeeid);
ALTER TABLE manages
ADD CONSTRAINT FK_manages_branch FOREIGN KEY(branchid) REFERENCES branch(branchid);
ALTER TABLE manages
ADD CONSTRAINT FK_manages_job FOREIGN KEY(jobid) REFERENCES job(jobid);
ALTER TABLE manages
ADD CONSTRAINT FK_manages_manager FOREIGN KEY(managerid) REFERENCES job(managerid);
Your ER-Diagram is missing an important information: the cardinality between the manager and the new entity that is built from the other 4 elements job, employee, manager, branch and works-on (this new entity is marked by the square around them).
From the quote on the slides we can deduce that it is a 0..1-relationship,
that means every combination of job, branch and employee (or every entry in works-on) has at most one manager, but does not require one (in contrast to e.g. a composition).
But you will have to verify that cardinality in your actual task.
You can oftentimes implement an ER-diagram in several ways, but the slides imply the following realization:
CREATE TABLE manages
( jobid INT not null,
branchid INT not null,
employeeid INT not null,
managerid INT null,
PRIMARY KEY (jobid, branchid, empoyeeid)
);
I omitted the trivial foreign keys to the tables job, employee, manager and branch.
With this implementation, you do not have an explicit table for the works-on-relation anymore, just like the second statement in your slides says. It is included in the manages table. That's only possible for a 0..1-relation, which is why that cardinality was deducable.
If you want to keep a table for works-on, you would use
CREATE TABLE works_on
( jobid INT not null,
branchid INT not null,
employeeid INT not null,
PRIMARY KEY (jobid, branchid, empoyeeid)
);
CREATE TABLE manages
( jobid INT not null,
branchid INT not null,
employeeid INT not null,
managerid INT not null,
PRIMARY KEY (jobid, branchid, empoyeeid),
FOREIGN KEY (jobid, branchid, employeeid)
REFERENCES works_on (jobid, branchid, employeeid)
);
Again, I omitted the trivial foreign keys.
To simplify the foreign key (and maybe to emphasize that the combination is considered a new entity), you can, as #Barmar proposed, add an additional (usually auto-increment) key to the works_on-table and use this value in the manages-table, although the slides do not do this here.
In case you need to implement a 0..n-relation (several manager can manage a specific works-on-combination), you cannot absorb the works-on-relation in the manages-relation anymore (so you need both tables), and to respect the n, you will have to include managerid in the primary key, PRIMARY KEY (jobid, branchid, empoyeeid, managerid) (but still need to keep the FOREIGN KEY (jobid, branchid, employeeid)).
You need to give a primary key to the works_on table, and then reference that in the manages table, rather than referencing employee, job, and branch directly.
CREATE TABLE works_on (
works_on_id INT PRIMARY KEY,
jobid INT,
branchid INT,
employeeid INT,
CONSTRAINT jobid FOREIGN KEY REFERENCES job(jobid),
CONSTRAINT branchid FOREIGN KEY REFERENCES brahc(branchid),
CONSTRAINT employeeid FOREIGN KEY REFERENCES employee(employeeid)
);
CREATE TABLE manages (
managerid INT,
works_on_id INT,
CONSTRAINT managerid FOREIGN KEY REFERENCES manager(id),
CONSTRAINT works_on_id FOREIGN KEY REFERENCES works_on(id)
)

Foreign Key Constraint Error - MySQL

I am trying to create a database containing information from a game for an assignment but am having issues when creating the tables with assigning the primary and foreign keys. Any help would be amazing.
The error code I am getting is: #1215 - Cannot add foreign key constraint on table Boss. It creates the first table fine it just stops working and throws the error at the foreign key when creating table boss.
Within the item table the field Boss needs to be able have the same data entered for multiple different items.
CREATE TABLE item ( ID SERIAL, Name VARCHAR(35), Boss VARCHAR(25), Type VARCHAR(20), Slot VARCHAR(20), PRIMARY KEY (Name,Boss) );
CREATE TABLE boss ( ID SERIAL, Boss VARCHAR(25), Type VARCHAR(20), Location VARCHAR(20), Difficulty INT, PRIMARY KEY (ID, Location), FOREIGN KEY (Boss) REFERENCES item(Boss) );
CREATE TABLE dungeon ( ID SERIAL, Name VARCHAR(25), Location VARCHAR(20), Rating INT, PRIMARY KEY (Name), FOREIGN KEY (Location) REFERENCES boss(Location) );
I can see many things wrong with your db creation script, but I will only stick to the ones that immediately affect getting it to work in the first place:
You should always refer to another table through its primary key and nothing else.
Your references are put in the wrong tables. If you want to refer to a "boss" from "item" then you should do just that and not refer to "item" from "boss".
But the most important thing is that references should be of the same type as the primary keys they refer to.
A composite key is a bad idea when you don't really need one.
So, a working script would be:
CREATE TABLE dungeon ( ID BIGINT(20), Name VARCHAR(25), Location VARCHAR(20), Rating INT, PRIMARY KEY (ID) );
CREATE TABLE boss ( ID BIGINT(20), Type VARCHAR(20), LocationId BIGINT(20), Difficulty INT, PRIMARY KEY (ID), FOREIGN KEY (LocationId) REFERENCES dungeon (ID) );
CREATE TABLE item ( ID BIGINT(20), Name VARCHAR(35), BossId BIGINT(20), Type VARCHAR(20), Slot VARCHAR(20), PRIMARY KEY (ID), FOREIGN KEY (BossId) REFERENCES boss (ID) );
You can adjust it to your needs, but before doing that, I would suggest reading a good book on database design or -at least- a tutorial on basic relational databases principles.

Unable to alter table with foreign key

My two empty tables:
CREATE TABLE person (
person_id SMALLINT UNSIGNED,
fname VARCHAR(20),
lname VARCHAR(20),
gender ENUM('m', 'f'),
birth_date DATE,
street VARCHAR(30),
city VARCHAR(20),
state VARCHAR(20),
country VARCHAR(20),
postal_code VARCHAR(20),
CONSTRAINT pk_person PRIMARY KEY (person_id)
);
create TABLE favorite_food (
person_id SMALLINT UNSIGNED,
food VARCHAR(20),
CONSTRAINT pk_favorite_food PRIMARY KEY (person_id, food),
CONSTRAINT fk_fav_food_person_id FOREIGN KEY (person_id) REFERENCES person (person_id)
);
Needed modification:
ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;
Result:
ERROR 1833 (HY000): Cannot change column 'person_id': used in a
foreign key constraint 'fk_fav_food_person_id' of table
'tom.favorite_food'
Why is this and is there a way around this without dropping the tables and redefining them?
This is probably because there already is data in person.person_id in any rows (NOT NULL). You can circumvent this by disabling foreign key checks
I believe the best way to handle this is to drop the foreign key reference from favorite_food, alter the column in person and then recreate the foreign key reference. That will properly recreate the index on which the key depends.

Error 1215: Why is it happening?

I am confused as to why error 1215: cannot add foreign key constraint is occurring in my code. Does anybody have any ideas?
The first four tables create themselves fine, but the error is thrown when I try to create the Stars table. I am not sure what's going wrong, as the keys being referenced are primary keys and so they must be unique and non-null. Plus, those keys exist as the previous tables have been created first. It might just be a stupid mistake I made, but its better to have fresh eyes look at it, right?
This database is simplistic as it is because I'm doing it for a school assignment.
create table MovieExec
(
execName varchar(40),
certNum numeric(30, 0),
address varchar(50),
networth real,
primary key(execName),
unique key(certNum)
);
create table Stud
(
studName varchar(30),
address varchar(50),
presCNum numeric(30, 0),
primary key(studName),
foreign key (presCNum) references MovieExec(certNum)
);
create table MovieStar(
starName varchar(30),
address varchar(50),
gender varchar(1),
birthdate date,
primary key(starName)
);
create table Movies
(
movieTitle varchar(30),
movieYear numeric(4,0),
length numeric(3,0),
genre varchar(30),
studioName varchar(30),
producerCNum numeric(30, 0),
primary key (movieTitle, movieYear),
foreign key (producerCNum) references MovieExec(certNum),
foreign key (studioName) references Stud(studName)
);
create table Stars
(
movieTitle varchar(30),
movieYear numeric(4,0),
starName varchar(30),
primary key (movieTitle, movieYear, starName),
foreign key (movieTitle) references Movies(movieTitle),
foreign key (movieYear) references Movies(movieYear),
foreign key (starName) references MovieStar(starName)
);
It should be a composite foreign key for the title and the year:
foreign key (movieTitle, movieYear) references Movies(movieTitle, movieYear),
foreign key (starName) references MovieStar(starName)
It should be composite because that's the exact PK of Movies table and that's the only UNIQUE combination there currently available.
Then as #CBroe mentioned in their answer it would be a good idea to index the Stars.starName column.
http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html:
“[…] in the referenced table, there must be an index where the referenced columns are listed as the first columns in the same order”
In your Movies table, you only have a primary key on the combination (movieTitle, movieYear) – but movieYear is not the first column in that key, and therefor
foreign key (movieYear) references Movies(movieYear)
in your CREATE statement for the stars table fails.
Add a key on Movies.movieYear – then creating a foreign key referencing that column on the stars table will work.
FYI: MySQL has a YEAR data type – you should use that, instead of a NUMERIC for your movie year.
And you are mixing singular and plural in your table names. Convention is to use the singular of the object that a table holds as table name; but at least you should try and be consistent with your naming scheme.