SQL Many-to-Many Relationship Between Multiple Tables - mysql

I have a database I'm trying to create on SQL and I am trying to connect the relationships together. There are three tables: superhero, power, and superheroPower. The tables superhero and power is a many to many relationship which is represented by the table superheroPower.
Is the syntax below correct for foreign keys between tables (and everything else)? Also, is there any other recommendations on these tables in terms of their setup?
CREATE TABLE superhero( id INT NOT NULL AUTO_INCREMENT,
heroName VARCHAR(255) NOT NULL,
firstName VARCHAR(255),
lastName VARCHAR(255),
firstAppearance DATE,
gender VARCHAR(255),
bio TEXT,
universe VARCHAR(255),
PRIMARY KEY(id)
) ENGINE=InnoDB;
CREATE TABLE power(
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
PRIMARY KEY(id)
) ENGINE=InnoDB;
CREATE TABLE superheroPower(
superheroID INT,
powerID INT,
PRIMARY KEY(superheroID, powerID),
FOREIGN KEY(superheroID) REFERENCES superhero(id),
FOREIGN KEY(powerID) REFERENCES power(id)
) ENGINE=InnoDB;

Yes, everything there looks okay. But...
A few notes:
We'd use a shorter datatype for the gender column; I don't see that we'd need 255 characters to express that. (There is a limit on the maximum size of a row which is enforced.) If there only a few values for that, we'd consider ENUM datatype.
We'd also likely add NOT NULL constraints on several of those columns, such as heroname, firstname, lastname. We'd also likely add DEFAULT ''. Sometimes, we really do need to allow NULL values for some reason, but we use NOT NULL wherever we can.
I'm hesitant about the TEXT columns. There's nothing wrong with using TEXT datatype, but I'm just suspicious that those may be "hiding" some information that might better be stored in additional columns.
For the foreign keys, we'd assign a name to the constraints, following the pattern we use, and also likely add ON UPDATE CASCADE ON DELETE CASCADE
CONSTRAINT FK_superheroPower_power FOREIGN KEY (powerID)
REFERENCES power(id) ON UPDATE CASCADE ON DELETE CASCADE
A note about identifiers (table names and column names)
The way we do it, all table name are lower case. (We have a MySQL option set that forces all table names to lower case.) We do this to avoid incompatibility issues for different operating systems/filesystems (some of which are case sensitive, and some are not).
Also, table names are singular. The name of the table names what one row of the table represents. We also don't include _table as part of the name.
Column names in MySQL are never case sensitive, but we always use lower case for the column names as well. We don't "camelCase" our column names, we use underscore character as separators, e.g. power_id vs. powerID, hero_name vs. heroName.
FOLLOWUP
My "notes" above aren't specific rules that must be followed; those are just patterns we use.
Following these patterns does not guarantee that we'll have a successful software, but it does help us.
For your reference, I'll show how these tables would look as a "first cut" from our shop, as an illustration of another pattern; this is not "the right way", it's just "a way" that we've settled on as a team.
CREATE TABLE superhero
( id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'pk'
, hero_name VARCHAR(255) NOT NULL COMMENT ''
, first_name VARCHAR(255) NOT NULL DEFAULT '' COMMENT ''
, last_name VARCHAR(255) NOT NULL DEFAULT '' COMMENT ''
, first_appearance DATE COMMENT 'date superhero first appeared'
, gender ENUM('female','male','other') COMMENT 'female,male or other'
, biography_text TEXT COMMENT ''
, universe VARCHAR(255) COMMENT ''
, PRIMARY KEY(id)
, UNIQUE KEY superhero_UX1 (hero_name)
) ENGINE=InnoDB;
CREATE TABLE power
( id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'pk'
, name VARCHAR(255) NOT NULL COMMENT ''
, description_text TEXT NOT NULL COMMENT ''
, PRIMARY KEY(id)
, UNIQUE KEY power_UX1 (name)
) ENGINE=InnoDB;
CREATE TABLE superheropower
( superhero_id INT UNSIGNED NOT NULL COMMENT 'pk, fk ref superhero'
, power_id INT UNSIGNED NOT NULL COMMENT 'pk, fk ref power'
, PRIMARY KEY(superhero_id, power_id)
, CONSTRAINT FK_superheropower_superhero
FOREIGN KEY(superhero_id) REFERENCES superhero(id)
ON UPDATE CASCADE ON DELETE CASCADE
, CONSTRAINT FK_superheropower_power
FOREIGN KEY (power_id) REFERENCES power(id)
ON UPDATE CASCADE ON DELETE CASCADE
) ENGINE=InnoDB;

Your design seems on the right track, this is the tables i would have gone with - adding some Indexes for the fields its likely that you will search for and adding the actions needed for the CONSTRAINT keys
CREATE TABLE `_superhero` (
`id` int(11) NOT NULL auto_increment,
`heroName` varchar(255) NOT NULL,
`firstName` varchar(255) default NULL,
`lastName` varchar(255) default NULL,
`firstAppearance` date default NULL,
`gender` enum('Other','Female','Male') default NULL,
`bio` text,
`universe` varchar(255) default NULL,
PRIMARY KEY (`id`),
KEY `indxHname` (`heroName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `_power` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`description` text NOT NULL,
PRIMARY KEY (`id`),
KEY `indx4` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `_superheropower` (
`superheroID` int(11) NOT NULL default '0',
`powerID` int(11) NOT NULL default '0',
PRIMARY KEY (`superheroID`,`powerID`),
KEY `indx1` (`superheroID`),
KEY `indx2` (`powerID`),
CONSTRAINT `fk2` FOREIGN KEY (`powerID`) REFERENCES `_power` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
CONSTRAINT `fk1` FOREIGN KEY (`superheroID`) REFERENCES `_superhero` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Edit[1]: This is a version of SQL code on how I would do it!!
CREATE TABLE superhero
(
Superheo_id INT NOT NULL AUTO_INCREMENT,
heroName VARCHAR(255) NOT NULL,
firstName VARCHAR(255)NULL,
lastName VARCHAR(255)NULL,
firstAppearance DATE NULL,
gender VARCHAR(255) NULL,
bio TEXT NULL,
universe VARCHAR(255) NULL,
CONSTRAINT SUPERHERO_PK PRIMARY KEY(SUPERHERO_id)
);
CREATE TABLE power
(
POWER_id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
CONSTRAINT POWER_PK PRIMARY KEY(POWER_id)
);
CREATE TABLE superheroPower
(
superheroID INT DEFAULT(0) NOT NULL,
powerID INT DEFAULT(0) NOT NULL,
CONSTRAINT SUPERHEROPOWERS_SUPERHERO_FK FOREIGN KEY(superheroID) REFERENCES superhero(id),
CONSTRAINT SUPERHEROPOWERS_POWERS_FK FOREIGN KEY(powerID) REFERENCES power(id)
);
edit[2]:You are able to change null to not null and vise versa depending on if you want a user to move past with out installing the other information. I have never used the Auto_increment before in my sql tables, so for me that is something new I just learned from you

Related

MySQL Error - Cannot add foreign key constraint

I know this question has been asked several times but it seemed like the problem was due to different data types between parent and child rows.
In my case, the data types are the same but I'm still getting the error. Here's my code
CREATE TABLE STUDENT_2(
StudentNumber INT NOT NULL AUTO_INCREMENT,
StudentName VARCHAR(50) NULL,
Dorm VARCHAR(50) NULL,
RoomType VARCHAR(50) NOT NULL,
CONSTRAINT STUDENTPK PRIMARY KEY(StudentNumber)
);
CREATE TABLE DORM_COST(
RoomType VARCHAR(50) NOT NULL,
DormCost DECIMAL(7,2) NULL,
CONSTRAINT DORM_COSTPK PRIMARY KEY(RoomType),
CONSTRAINT DORM_COST_FK FOREIGN KEY(RoomType)
REFERENCES STUDENT_2(RoomType)
ON UPDATE CASCADE
ON DELETE CASCADE
);
Where DORM_COSTS' foreign key cannot be added.
Thanks!
You want the foreign key reference on the table that has the foreign key, not the primary key. So, that would be:
CREATE TABLE DORM_COST (
RoomType VARCHAR(50) NOT NULL,
DormCost DECIMAL(7,2) NULL,
CONSTRAINT DORM_COSTPK PRIMARY KEY(RoomType)
);
CREATE TABLE STUDENT_2(
StudentNumber INT NOT NULL AUTO_INCREMENT,
StudentName VARCHAR(50) NULL,
Dorm VARCHAR(50) NULL,
RoomType VARCHAR(50) NOT NULL,
CONSTRAINT STUDENTPK PRIMARY KEY(StudentNumber),
CONSTRAINT fk_student2_roomtype FOREIGN KEY (RoomType) REFERENCES DORM_COST(RoomType)
);
Here is a db<>fiddle that shows that this works.
That said your data model seems quite strange.
I would expect a table called RoomTypes to have a primary key of RoomTypeId.
I would expect dorm_cost to have dates, because costs can vary from year to year.
I would expect different dorms to have similar room types -- singles, doubles, and so on.
I would expect those room types to vary, perhaps, by dorm.

errno: 150 "Foreign key constraint is incorrectly formed" Nothing helps

sorry for maybe stupid question, but I stack with my sql query. Tried out many methods to avid it but still have error 150. I've created 3 tables and one with foreighn keys:
Table users
create table users(
id int(11) primary key auto_increment,
unique_id varchar(23) not null unique,
name varchar(50) not null,
cname varchar(50) not null,
email varchar(100) not null unique,
encrypted_password varchar(80) not null,
salt varchar(10) not null,
created_at datetime,
updated_at datetime null
);
Table behaviours
CREATE TABLE behaviours (
id int(2) AUTO_INCREMENT PRIMARY KEY,
bName varchar(100) NOT NULL
);
Table severytys
CREATE TABLE severitys (
id int(2) AUTO_INCREMENT PRIMARY KEY,
severity varchar(10) NOT NULL
);
And here it shows errors:
CREATE TABLE child_behaviours(
id int(11) primary key auto_increment,
name varchar(50) not null,
cname varchar(50) not null,
bName varchar(100) NOT NULL,
severity varchar(10) NOT NULL,
start_at datetime,
stop_at datetime null,
FOREIGN KEY (id) REFERENCES users(id),
FOREIGN KEY (bName) REFERENCES behaviours(bName),
FOREIGN KEY (severity) REFERENCES severitys(severity)
);
Will be really pleasent to have an answer to this issue
You can only create a FK on a column that is both UNIQUE and NOT NULL. This is usually, but not always, the primary key of the referenced table.
If you reference anything other than the PK, you need a really really good reason.
(I'd even add to that, that you need a really really good reason to use anything other than an INT or a couple INTs as a PK...)
Now, child_behaviours has several errors:
Duplication of columns already present in users. If you reference users by its id, there is no need to duplicate the columns.
Reference to behaviors(bName) instead of using its id
same thing for reference to severity
Also, there is the generic mistake of using "id" columns. Please call them "user_id", "severity_id", etc, and then call them the same in your references. This will make your life much easier, and avoid stuff like:
SELECT foo.id AD foo_id, bar.id AS bar_id FROM foo JOIN bar ON ...
You have to refer to a primary key, when building a foreign key
CREATE TABLE child_behaviours(
userId int not null,
behaviourId int NOT NULL,
severityId int NOT NULL,
start_at datetime not null,
stop_at datetime,
FOREIGN KEY (userId) REFERENCES users(id),
FOREIGN KEY (behaviourId) REFERENCES behaviours(id),
FOREIGN KEY (severityId) REFERENCES severitys(id)
);

How do I add a foreign key to a table in Sequel Pro?

I am trying to add a foreign key to a table in Sequel Pro (using the UI).
I have two tables: "titles" and "categories" as below:
CREATE TABLE `titles` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` tinytext NOT NULL,
`category` varchar(256) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `category` (
`key` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(256) NOT NULL DEFAULT '',
PRIMARY KEY (`key`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
I want to create a foreign key, but nothing I try works.
The category table should be a simple lookup table. I want to assign each title a category from about 6 - 8 different choices.
Originally I had the category fields as tinytext, but I would get the error:
"MySQL Error 1170 (42000): BLOB/TEXT Column Used in Key Specification Without a Key Length".
Searched here and discovered you can't use text field that way, so I switched to Varchar and added a length of 256. Now I get:
MySQL said: Can't create table 'lit.#sql-2bf3_2' (errno: 150).
How can I create a foreign key for my table?
In Access this is pretty easily done. Somehow Access associates the unique key in the table with the lookup, but then hides the key and shows you the text field instead. How can I get a similar result with Sequel Pro and MySQL?
EDIT:
So, to clarify this is where I'm at right now. I've added an index on the category field in the titles table (first picture).
I've changed the "key" field in the category table to CategoryID (second picture).
However, I still can't seem to create the relationship between the two tables. I get the same error
As category will be your lookup table off of titles, you'd need to create an index on category which would refer to the foreign key. They would both need to be the same datatype (usually an INT, though sometimes you could use a CHAR(2) variable in some cases, but usually not necessary). Since you only expect 6-8 categories, I'd make it INT(1) (or may be INT(2) to be safe).
In this case, you would need to create something like categoryId which would first need to be indexed, then connect to the foreign key on categorywhich does not appear to exist; I'm not sure you want to use a term like key. Why not just make categoryId the primary key on category? this way when you create the foreign key on titles with the same name, it should link up fine.
Edit:
To clarify a little, after you've created categoryID on category you can do this under titles
ALTER TABLE Orders
ADD CONSTRAINT fk_categoryID
FOREIGN KEY (`categoryId`) REFERENCES `category`(`categoryId`)
Edit:
Here's a modification using your original layout. this should work for you:
CREATE TABLE IF NOT EXISTS `category` (
`key` int(2) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(256) NOT NULL DEFAULT '',
PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
CREATE TABLE IF NOT EXISTS `titles` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` tinytext NOT NULL,
`categoryID` int(2) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `categoryID` (`categoryID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
ALTER TABLE `titles`
ADD CONSTRAINT `titles_ibfk_1` FOREIGN KEY (`categoryID`) REFERENCES `category` (`key`);

Unique constraint validation order MySql/MariaDb

Just ran into an interesting question about UNIQUE CONSTRAINT validation orders.
Is it possible to tell Mysql(or MariaDB) to validate one unique constraint before the other (id before email)?
CREATE TABLE IF NOT EXISTS users (
`pk` int unsigned NOT NULL AUTO_INCREMENT,
`id` VARCHAR(50) NOT NULL,
`email` VARCHAR(100) NOT NULL,
`name` VARCHAR(100) NOT NULL,
`password` VARCHAR(100),
PRIMARY KEY (pk),
UNIQUE INDEX id (id),
UNIQUE INDEX email (email),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
If the id constraint fails it is a duplicate submission and if the email fails it's a business failure creating a second user with an already registered email.
Ofcourse it could be solved by searching for it first but this would somehow be smoother.

MySQL creating a table (errno 150)

I would like to ask something that troubles me many many days...
Here is what I mean:
I create these two tables:
CREATE TABLE IF NOT EXISTS journal (
issn varchar(20) NOT NULL,
j_title varchar(100) NOT NULL,
j_publisher varchar(30) NOT NULL,
PRIMARY KEY (issn)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS volume (
volume_no int(11) NOT NULL,
issn varchar(20) NOT NULL,
year int(11) NOT NULL,
PRIMARY KEY (issn,volume_no),
FOREIGN KEY (issn) REFERENCES journal(issn)
) ENGINE=InnoDB;
When I try to create this:
CREATE TABLE IF NOT EXISTS issue (
issue_no int(11) NOT NULL,
issue_pages varchar(10) NOT NULL,
issue_date varchar(10) NOT NULL,
issn varchar(20) NOT NULL,
volume_no int(11) NOT NULL,
PRIMARY KEY (issue_no,issn,volume_no),
FOREIGN KEY (issn) REFERENCES journal(issn),
FOREIGN KEY (volume_no) REFERENCES volume(volume_no)
) ENGINE=InnoDB;
it throws an error (errno 150)
The error is in the foreign key volume_no.
Without FOREIGN KEY (volume_no) REFERENCES volume(volume_no)
the table is created without a problem.... I can't explain what's going on... I have seen it many times again and again but nothing!! Does anybody know what's going on?
Thanks in advance!!
I could see that the foreign key doesnt include issn but which is actually included in primary key for volumn table.
CREATE TABLE IF NOT EXISTS issue ( issue_no int(11) NOT NULL,
issue_pages varchar(10) NOT NULL,
issue_date varchar(10) NOT NULL,
issn varchar(20) NOT NULL,
volume_no int(11) NOT NULL,
PRIMARY KEY (issue_no,issn,volume_no),
FOREIGN KEY (issn,volume_no) REFERENCES volume(issn,volume_no) ) ENGINE=InnoDB;
Look at the below sql fiddle.
http://sqlfiddle.com/#!2/55a63
maybe volume_no needs to be UNSIGNED
FOREIGN keys must reference a PRIMARY or a UNIQUE key in the parent table.
You need only one Foreign Key at table issue, not two. And it should reference the Primary Key of volume:
CREATE TABLE IF NOT EXISTS issue (
issue_no int(11) NOT NULL,
issue_pages varchar(10) NOT NULL,
issue_date varchar(10) NOT NULL,
issn varchar(20) NOT NULL,
volume_no int(11) NOT NULL,
PRIMARY KEY (issn, volume_no, issue_no),
FOREIGN KEY (issn, volume_no)
REFERENCES volume(issn, volume_no)
) ENGINE=InnoDB;
If the PK of the parent table is more than one field, the order of the fields in the FK must be the same as the order in the PK.
issue: FOREIGN KEY (issn, volume_no) REFERENCES volume(issn, volume_no)
These conditions must be satisfied to not get error 150:
The two tables must be ENGINE=InnoDB.
The two tables must have the same charset.
The PK column(s) in the parent table and the FK column(s) must be the same data type.
The PK column(s) in the parent table and the FK column(s), if they have a define collation type, must have the same collation type;
If there is data already in the foreign key table, the FK column value(s) must match values in the parent table PK columns.
source: MySQL Creating tables with Foreign Keys giving errno: 150
I had about the same issue with my database. It wasn't about the definition of the foreign key, actually it was the definition of the primary key field.
CREATE TABLE tblProcesses (
fldProcessesID SMALLINT(5) UNIQUE NOT NULL AUTO_INCREMENT ,
CREATE TABLE tblProcessesMessage (
fldProcesses TEXT NOT NULL,
fldProcessesID VARCHAR(15) NOT NULL DEFAULT ,
The primary key in tblProcesses (fldProcessesID) did not have the UNSIGNED keyword while the foreign key in tblProcessesMessage (fldProcessesID) had the UNSIGNED keyword. This keyword was causing the problem - inconsistent type of field. So i added the UNSIGNED keyword to fldProcessesID in tblPreocesses:
CREATE TABLE tblProcesses (
fldProcessesID SMALLINT(5) UNSIGNED UNIQUE NOT NULL AUTO_INCREMENT,
I hope that this will help you solve your problems.
Best regards,
Nicholas