Unique constraint validation order MySql/MariaDb - mysql

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.

Related

How to add two foreign keys and add values in child table with reference of parent table

I have 3 tables in my database USERS, QUESTION, and ANSWER. I want QUESTION table to interact with USERS and ANSWER table and ANSWER table to USER and QUESTION table. So far I have related QUESTION table with USERS table using foreign key and ANSWER table with QUESTION table.
When I am trying to add second foreign key in ANSWER table in mysql I am getting an error
a) Duplicate foreign key constraint name 'userid'
Even if I am trying to add using query it still giving me an error.
Can I add values in QUESTION table in reference with USERS table with help of foreign key? for e.g. i have user in USERS table with userid 1 i want to add values in QUESTION table where quesid will be 1 and also userid also be 1. Till now I have tried this query
INSERT INTO users_ques
SELECT "What is JAVA","Please Share the details" FROM users WHERE quesid = 1;
Below are my tables and their script
USERS TABLE
CREATE TABLE `users` (
`userid` int NOT NULL AUTO_INCREMENT,
`username` varchar(45) NOT NULL,
`password` varchar(45) NOT NULL,
`firstname` varchar(45) NOT NULL,
`lastname` varchar(45) DEFAULT NULL,
`email` varchar(45) DEFAULT NULL,
`address` varchar(45) DEFAULT NULL,
`phone` int DEFAULT NULL,
PRIMARY KEY (`userid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
QUESTION TABLE
CREATE TABLE `users_ques` (
`quesid` int NOT NULL AUTO_INCREMENT,
`ques` varchar(50) NOT NULL,
`ques_desc` varchar(150) NOT NULL,
PRIMARY KEY (`quesid`),
CONSTRAINT `userid` FOREIGN KEY (`quesid`) REFERENCES `users` (`userid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
ANSWER TABLE
CREATE TABLE `users_ans` (
`ansid` int NOT NULL AUTO_INCREMENT,
`ans` varchar(200) NOT NULL,
PRIMARY KEY (`ansid`),
CONSTRAINT `quesid` FOREIGN KEY (`ansid`) REFERENCES `users_ques` (`quesid`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Recommended structure (use as a base, adjust to real data):
CREATE TABLE user (
user_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, -- unique index
username VARCHAR(255) NOT NULL,
password VARBINARY(255) NOT NULL, -- password hash, binary
firstname VARCHAR(255) NOT NULL,
lastname VARCHAR(255) DEFAULT NULL,
email VARCHAR(255) DEFAULT NULL,
address VARCHAR(255) DEFAULT NULL,
phone BIGINT DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE question (
question_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, -- unique index
question_text TEXT NOT NULL,
user_id INT NOT NULL, -- id of the user who have asked
CONSTRAINT user_to_question FOREIGN KEY (user_id) REFERENCES user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE answer (
answer_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, -- unique index
answer_text TEXT NOT NULL,
question_id INT NOT NULL, -- what question it answers on
user_id INT NOT NULL, -- id of the user who have answered
CONSTRAINT user_to_answer FOREIGN KEY (user_id) REFERENCES user (user_id),
CONSTRAINT question_to_answer FOREIGN KEY (question_id) REFERENCES question (question_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
In this scheme I do not show the restriction that the user cannot answer his own question. I consider such a restriction to be meaningless. But if you need in it nevertheless then you may add it by creating 2 triggers (BEFORE INSERT and BEFORE UPDATE) on answer table.
The error you are getting is exactly as it says, you are trying to add another constraint with the symbol userid but you already have one with that name on the users_ques table.
As per 13.1.20.5 FOREIGN KEY Constraints
The CONSTRAINT symbol value, if defined, must be unique in the
database. A duplicate symbol results in an error similar to: ERROR
1005 (HY000): Can't create table 'test.fk1' (errno: 121).
So, you either need to assign a database unique name to your constraint or allow the server to assign one for you. If you want to explicitly name them use expressive names that are unlikely to collide. Something like FK_<table_name>_<column_name>.
Also, probably a copy and paste error, but your users_ans looks completely wrong. ansid appears to be typical surrogate key but is then used as the foreign key to users_ques.quesid in your constraint. It looks like you are missing userid and quesid. There's a similar issue with your constraint and columns on users_ques
/* Allowing the server to create the indices and auto-name the constraints */
CREATE TABLE `users_ans` (
`ansid` int UNSIGNED NOT NULL AUTO_INCREMENT,
`quesid` int UNSIGNED NOT NULL,
`userid` int UNSIGNED NOT NULL,
`ans` varchar(200) NOT NULL,
PRIMARY KEY (`ansid`),
FOREIGN KEY (`quesid`)
REFERENCES `users_ques` (`quesid`),
FOREIGN KEY (`userid`)
REFERENCES `users` (`userid`)
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/* Explicitly naming indices and constraints */
CREATE TABLE `users_ans` (
`ansid` int UNSIGNED NOT NULL AUTO_INCREMENT,
`quesid` int UNSIGNED NOT NULL,
`userid` int UNSIGNED NOT NULL,
`ans` varchar(200) NOT NULL,
PRIMARY KEY (`ansid`),
INDEX `idx_users_ans_quesid` (quesid),
INDEX `idx_users_ans_userid` (userid),
CONSTRAINT `FK_users_ans_quesid`
FOREIGN KEY `idx_users_ans_quesid` (`quesid`)
REFERENCES `users_ques` (`quesid`),
CONSTRAINT `FK_users_ans_userid`
FOREIGN KEY `idx_users_ans_userid` (`userid`)
REFERENCES `users` (`userid`)
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
If users can only have one answer per question then you can drop the current surrogate key in favour of the natural key on (quesid, userid) -
/* This option only applies if a user can only answer a question once */
CREATE TABLE `users_ans` (
`quesid` int UNSIGNED NOT NULL,
`userid` int UNSIGNED NOT NULL,
`ans` varchar(200) NOT NULL,
PRIMARY KEY (`quesid`, `userid`),
INDEX `idx_inv_primary` (`userid`, `quesid`), -- probably needed for joins in both directions and will be used by `FK_users_ans_userid`
CONSTRAINT `FK_users_ans_quesid`
FOREIGN KEY (`quesid`)
REFERENCES `users_ques` (`quesid`)
ON DELETE RESTRICT
ON UPDATE RESTRICT,
CONSTRAINT `FK_users_ans_userid`
FOREIGN KEY (`userid`)
REFERENCES `users` (`userid`)
ON DELETE RESTRICT
ON UPDATE RESTRICT
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
As show in this last example, I prefer to include the referential actions, even when using the default, as it makes the intent clear.
Here's a little db<>fiddle
Your QUESTION table seems to be inappropriately named. As per your title above the create table statement, questions would appear to be the obvious name for the table.
I do not understand your second question, nor why you would want to add the userid to the "QUESTION TABLE". If you would like more help please add more detail to your question to clarify the issues.

Is it required to specify a Constraint Name when adding a Foreign Key in MySQL?

So far I've created the tables manually like this and I didn't have any issues.
CREATE TABLE users (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
email VARCHAR(100) NOT NULL,
cityId BIGINT(20) UNSIGNED NULL DEFAULT NULL,
PRIMARY KEY (id),
FOREIGN KEY (cityId)
REFERENCES cities (id));
Now, I'm using MySQL Workbench that generates the SQL Script for me, and the SQL script looks a bit different.
CREATE TABLE users (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
email VARCHAR(100) NOT NULL,
cityId BIGINT(20) UNSIGNED NULL DEFAULT NULL,
PRIMARY KEY (id),
CONSTRAINT cityId
FOREIGN KEY (cityId)
REFERENCES locations (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
I'm wondering if there is any difference between these two queries.
You are not required to provide a name for a foreign key, and if you don't, an autogenerated name will be assigned to it. Having said that, it's a good practice and will make your maintenance work easier.

Mapping (join) create table not allowing foreign keys?

I have these two tables for this site I'm building (login and licks). I want to allow the user to save his favorite licks so this means I need a mapping table but it's not working. It works without the foreign key constraint but I want/need the foreign key constraint. I've done research and everyone says to create the mapping table as I am but it's not working.
Can anyone tell me why this wont work? Thank you.
Table: Login
CREATE TABLE `login` (
`login_id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
PRIMARY KEY (`login_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
Table: Licks
CREATE TABLE `licks` (
`lick_id` int(11) NOT NULL AUTO_INCREMENT,
`lick_name` varchar(50) DEFAULT NULL,
`lick_category` varchar(20) DEFAULT NULL,
`lick_html_pg` varchar(50) DEFAULT NULL,
PRIMARY KEY (`lick_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
Table login_licks (Not woking!)
CREATE TABLE login_lick (
login_id INT NOT NULL,
lick_id INT NOT NULL,
PRIMARY KEY (login_id, lick_id),
FOREIGN KEY login_id REFERENCES login (login_id),
FOREIGN KEY lick_id REFERENCES licks (lick_id)
);
You are missing parentheses in the foreign key definition:
CREATE TABLE login_lick (
login_id INT NOT NULL,
lick_id INT NOT NULL,
PRIMARY KEY (login_id, lick_id),
FOREIGN KEY (login_id) REFERENCES login (login_id),
FOREIGN KEY (lick_id) REFERENCES licks (lick_id)
);
Here is a SQL Fiddle.

Error code 1005 - Can't create table

Having trouble adding a foreign key to my table.
CREATE TABLE event (
id BIGINT(20) NOT NULL AUTO_INCREMENT,
issued DATETIME NOT NULL,
user VARCHAR(255) NOT NULL,
subject VARCHAR(255) NOT NULL,
attending BIGINT(255) NOT NULL,
attendees VARCHAR(255) NOT NULL,
organisers VARCHAR(255) NOT NULL,
place BIGINT(20) NOT NULL,
started DATETIME NOT NULL,
stopped DATETIME NOT NULL,
content LONGTEXT NOT NULL,
broadcasting TINYINT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (place)
REFERENCES place (id),
FOREIGN KEY (user)
REFERENCES user (username)
)
The foreign key for place is executing fine but once I try adding user as a foreign key I keep getting the same error:
Error Code: 1005. Can't create table 'iservices.event' (errno: 150)
Can anyone help?
Picture of user table:
User table
Picture of place table:
Place table
Is there anyway of expanding the errors in MySQL Workbench?
a foreign key has to be unique, so make username unique (which is probably not the best idea) or choose something different like the user id.
My advice: add a primary auto-increment key to the user table and use it as the foreign-key.
Referenced column must be unique. It's OK in your case.
Columns in child and parent table must be of the same type. In your case they are different.
Could you please make sure user and place tables exist and have corresponding columns set as primary key? The below snipper works fine:
CREATE TABLE user(username VARCHAR(255) PRIMARY KEY);
CREATE TABLE place(id BIGINT(20) PRIMARY KEY);
CREATE TABLE event (
id BIGINT(20) NOT NULL AUTO_INCREMENT,
issued DATETIME NOT NULL,
user VARCHAR(255) NOT NULL,
subject VARCHAR(255) NOT NULL,
attending BIGINT(255) NOT NULL,
attendees VARCHAR(255) NOT NULL,
organisers VARCHAR(255) NOT NULL,
place BIGINT(20) NOT NULL,
started DATETIME NOT NULL,
stopped DATETIME NOT NULL,
content LONGTEXT NOT NULL,
broadcasting TINYINT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (place)
REFERENCES place (id),
FOREIGN KEY (user)
REFERENCES user (username)
)
Here's SQL Fiddle.
I had encountered a similar error.
This can occur when you are trying to add a non-primary key from the primary table as the foreign key in the secondary table.
To avoid this, the foreign key in the secondary table has to be the primary key in the primary table.

SQL Many-to-Many Relationship Between Multiple Tables

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