How do I make join table entries unique? - mysql

I know how to make an individual column entry unique, but I want to make two columns together unique. I have a join table of votes. Each vote has a userId and a messageId. Every vote is an entry into this table. I do not want to allow a user to vote more than once on a single message. What constraint can I add to this join table to make it so each user can only vote on each message or comment once?
CREATE TABLE messages (
id int(5) AUTO_INCREMENT,
messageString text NOT NULL,
image varchar(255) DEFAULT 'NULL',
score int(5) DEFAULT 0,
PRIMARY KEY (id)
);
CREATE TABLE comments (
id int(5) AUTO_INCREMENT,
commentString text NOT NULL,
messageId int(5) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (messageId) REFERENCES messages(id)
);
CREATE TABLE votes (
id int(5) AUTO_INCREMENT,
PRIMARY KEY(id),
userToken VARCHAR(255),
messageId int(5) DEFAULT 0,
commentId int(5) DEFAULT 0,
FOREIGN KEY (userToken) REFERENCES users(token),
FOREIGN KEY (messageId) REFERENCES messages(id),
FOREIGN KEY (commentId) REFERENCES comments(id)
);
CREATE TABLE users (
token VARCHAR(255),
PRIMARY KEY(token)
);

Either remove votes.id and use PRIMARY KEY (userToken, messageId), or use PRIMARY KEY (id), UNIQUE KEY (userToken, messageId) if you think you absolutely need an id.
(Not sure what commentId is though, couldn't figure it out from your explanation; maybe you'll need to add that to the composite index as well.)

Related

Error while trying to add multiple foreign keys to single table

I am trying to create a child table that constraints 3 foreign keys from the parent but I receive an error 1215: cannot add foreign key constraint
parent table:
CREATE TABLE `Availability` (
`time_of_day` varchar(20) NOT NULL,
`day_of_week` varchar(20) NOT NULL,
`email` varchar(60) NOT NULL,
PRIMARY KEY (`time_of_day`,`day_of_week`,`email`),
KEY `email` (`email`),
CONSTRAINT `Availability_ibfk_1` FOREIGN KEY (`email`) REFERENCES `service_provider` (`email_service_provider`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
child table (which I cant build due to error mentioned above):
CREATE TABLE TEST1
(
num_request INT NOT NULL,
time_of_day VARCHAR(20) NOT NULL,
day_of_week VARCHAR(20) NOT NULL,
email VARCHAR(60) NOT NULL,
PRIMARY KEY (num_request),
Foreign key (time_of_day) references Availability(time_of_day),
Foreign key (day_of_week) references Availability(day_of_week),
Foreign key (email) references Availability(email)
);
Please show me what I'm doing wrong... Thank you all.
When you're making a foreign key to a table with a composite primary key (i.e. a key of multiple columns), you should make the foreign key composite as well.
CREATE TABLE TEST1
(
num_request INT NOT NULL,
time_of_day VARCHAR(20) NOT NULL,
day_of_week VARCHAR(20) NOT NULL,
email VARCHAR(60) NOT NULL,
PRIMARY KEY (num_request),
Foreign key (time_of_day, day_of_week, email) references Availability(time_of_day, day_of_week, email)
)
The columns of the foreign key should match the columns of the primary or unique key they reference. They should have the same number of columns, in the same order.
What you tried to do was create three separate constraints of one column each.

SQL: Unique index for two columns where value of one is NOT DEFAULT

I have the following table structure:
CREATE TABLE `users` (
`id` varchar(36) NOT NULL,
`group_id` varchar(36) NOT NULL,
`group_owner` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`));
With group_owner acting as a boolean, I am trying to make a unique index to where any given group ID can only have one row where group_owner = 1.
Something like:
UNIQUE KEY (`group_id`,`group_owner`) ... WHERE group_owner NOT DEFAULT
How would I go about doing this?
You have the wrong structure for your data. What you need is a groups table. And that should have the foreign key reference:
CREATE TABLE `users` (
user_id varchar(36) PRIMARY KEY,
group_id varchar(36) NOT NULL
);
create table groups (
group_id int auto_increment primary key,
group_name varchar(36) not null,
owner_user_id varchar(36),
constraint fk_groups_owner foreign key (owner_user_id) references users (user_id)
);
alter table users add constraint fk_users_groups
foreign key (group_id) references groups(group_id);
Voila! Only one owner per group.
Notes:
I made group_id an integer. I find that auto-incremented values are better for primary keys. I would recommend doing the same for users as well.
This allows a user to be in only one group.
In practice, you will insert a group with a NULL owner. Then insert the user with the appropriate group, and then set the owner to the user.
Actually, the above data model runs the risk that a user could be the owner of a group s/he is not a member of. That is fixed with a slight tweak:
CREATE TABLE `users` (
user_id varchar(36) PRIMARY KEY,
group_id varchar(36) NOT NULL,
UNIQUE (group_id, user_id)
);
create table groups (
group_id int auto_increment primary key,
group_name varchar(36) not null,
owner_user_id varchar(36),
constraint fk_groups_owner foreign key (group_id, owner_user_id) references users (group_id, user_id)
);
alter table users add constraint fk_users_groups
foreign key (group_id) references groups(group_id);

MySQL Foreign Keys Issue

I have 3 table: CD, Song and Song_Details which is a relationship between CD & Song:
create table Song(
ID int not null auto_increment,
Title varchar(255) not null,
Length float not null,
primary key (ID, Title)
);
create table CD(
Title varchar(255) not null,
CD_Number int not null,
primary key (Title, CD_Number)
);
Create table Song_Details(
CD_Title varchar(255) not null,
Song_Title varchar(255) not null,
Track_Number int not null,
primary key(CD_Title, Song_Title),
foreign key(CD_Title) references CD(Title),
foreign key(Song_Title) references Song(Title)
);
I have managed to find out that this line in Song_Details:
foreign key(Song_Title) references Song(Title) is throwing the Error 1215(HY000): Cannot add foreign key constraint;
Could anyone help me see based on my table, what could be causing this issue?
Two things. The auto_increment key would normally be the foreign key. Second, you need to make your reference to all the keys defined as the primary or unique key for the table (I don't advise making foreign key references to non-unique keys although MySQL does all that).
So:
create table Song (
Song_ID int not null auto_increment,
Title varchar(255) not null,
Length float not null,
primary key (ID),
unique (title)
);
create table CD (
CD_Id int auto_increment primary key,
Title varchar(255) not null,
CD_Number int not null,
unique (Title, CD_Number)
);
Create table Song_Details(
CD_ID varchar(255) not null,
Song_Id varchar(255) not null,
Track_Number int not null,
primary key(CD_ID, Song_ID),
foreign key(CD_ID) references CD(CD_ID),
foreign key(Song_ID) references Song(Song_ID)
);
Notes:
Use the primary key relationships for the foreign key definitions.
I like to have the primary keys include the table name. That way, the primary key can have the same name as the corresponding foreign keys.
Don't put the titles in more than one place. They belong in the entity tables. Autoincremented ids can then be used to access the titles.

How do I make an SQL trigger that grabs the data that was passed into the query which activated the trigger?

I want to make it so when an entry is added to the votes table, whichever user the message/comment which was voted on gets an entry into their total_votes. I looked into sql triggers and it seems like it is what I want, but I am not sure how to get the information from the vote the user is creating in order to lookup the correct messageId and thus find the user who posted that message.
CREATE TABLE marks (
id int(5) AUTO_INCREMENT,
PRIMARY KEY (id),
x float(10, 6),
y float(10, 6),
z float(10, 6),
timestamp timestamp DEFAULT CURRENT_TIMESTAMP,
messageId int(5) NULL,
commentId int(5) NULL,
userToken VARCHAR(255),
FOREIGN KEY (messageId) REFERENCES messages(id),
FOREIGN KEY (commentId) REFERENCES comments(id),
FOREIGN KEY (userToken) REFERENCES users(token)
);
CREATE TABLE messages (
id int(5) AUTO_INCREMENT,
messageString text,
image varchar(255),
score int(5) DEFAULT 0,
PRIMARY KEY (id)
);
CREATE TABLE comments (
id int(5) AUTO_INCREMENT,
commentString text,
messageId int(5),
PRIMARY KEY (id),
FOREIGN KEY (messageId) REFERENCES messages(id)
);
CREATE TABLE votes (
id int(5) AUTO_INCREMENT,
PRIMARY KEY(id),
userToken VARCHAR(255),
messageId int(5) NULL,
commentId int(5) NULL,
FOREIGN KEY (userToken) REFERENCES users(token),
FOREIGN KEY (messageId) REFERENCES messages(id),
FOREIGN KEY (commentId) REFERENCES comments(id),
UNIQUE KEY (userToken, messageId),
UNIQUE KEY (userToken, commentId)
);
CREATE TABLE users (
token VARCHAR(255),
PRIMARY KEY(token),
total_votes int(5)
);
You refer to the new values in a trigger with the NEW. prefix. ie if you insert into a row that contains the field value, that field is available in the trigger as NEW.value, from there it is simple to construct an after insert trigger for your votes table. something like this:
DELIMITER //
create trigger vote_increment after insert on votes
for each row
begin
update users set total_votes = total_votes + 1 where token = NEW.user_token;
end//
DELIMITER ;
demo fiddle here
i'm not sure what your final logic needs to be, since you mention the messages table, and i don't see why that's necessary just to get the user to update their total, since the vote table contains userToken

Creating MySQL relationships with foreign keys and alter statements

I will try to be specific because I feel my understanding of the subject isn't quite precise as well. My problem is understanding how to create relations between tables with foreign keys which I add with alter statements. I have these tables.
CREATE TABLE article (
content TEXT NOT NULL,
published_on DATE NOT NULL,
created_on DATE NOT NULL,
);
CREATE TABLE category(
name VARCHAR(30) NOT NULL,
date_created DATE NOT NULL,
);
CREATE TABLE user(
income FLOAT(30, 30) NOT NULL,
password VARCHAR(30) NOT NULL,
picture_url TEXT NOT NULL,
);
CREATE TABLE tag(
description VARCHAR(30) NOT NULL,
second_priority FLOAT(30, 30) NOT NULL,
);
To which I have to make there relationships:
Tag has a one to one connection to Category
Category has a many to one connection to User
User has a one to many connection to Article
And to do that I use there statements:
ALTER TABLE tag ADD CONSTRAINT FOREIGN KEY (tag_id) REFERENCES category (category_id);
ALTER TABLE category ADD CONSTRAINT FOREIGN KEY (user_id) REFERENCES user (user_id);
ALTER TABLE user ADD CONSTRAINT FOREIGN KEY (user_id) REFERENCES article (user_id);
My problem is that the third one fails. When I switch the places of article and user the constraint passes. After a bit of digging and experimenting I found out that I can't constraint two keys from which one is either UNIQUE or PRIMARY KEY and the other one just NOT NULL. So far I don't know how to continue, can someone please enlighten me as to how to create a one to one, many to one, one to many and a many to many relationship between these tables because I am kinda lost and everything in my head is a mess.
FULL STUFF:
CREATE DATABASE exam_database;
USE exam_database;
CREATE TABLE article (
content TEXT NOT NULL,
published_on DATE NOT NULL,
created_on DATE NOT NULL,
user_id INT(30) NOT NULL
);
CREATE TABLE category(
name VARCHAR(30) NOT NULL,
date_created DATE NOT NULL,
category_id INT(30) NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE,
user_id INT(30) NOT NULL
);
CREATE TABLE user(
income FLOAT(30, 30) NOT NULL,
password VARCHAR(30) NOT NULL,
picture_url TEXT NOT NULL,
user_id INT(30) NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE
);
CREATE TABLE tag(
description VARCHAR(30) NOT NULL,
second_priority FLOAT(30, 30) NOT NULL,
tag_id INT(30) NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE
);
ALTER TABLE tag ADD CONSTRAINT FOREIGN KEY (tag_id) REFERENCES category (category_id);
ALTER TABLE category ADD CONSTRAINT FOREIGN KEY (user_id) REFERENCES user (user_id);
ALTER TABLE user ADD CONSTRAINT FOREIGN KEY (user_id) REFERENCES article (user_id);
First Your structure has some errors:
Missing PK, Why using INT(30)? etc...
Your DB should looks like:
CREATE DATABASE exam_database;
USE exam_database;
CREATE TABLE article (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE,
user_id INT NOT NULL,
content TEXT NOT NULL,
published_on DATE NOT NULL,
created_on DATE NOT NULL
);
CREATE TABLE category(
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE,
user_id INT NOT NULL,
name VARCHAR(30) NOT NULL,
date_created DATE NOT NULL
);
CREATE TABLE user (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE ,
income FLOAT(30, 30) NOT NULL,
password VARCHAR(30) NOT NULL,
picture_url TEXT NOT NULL
);
CREATE TABLE tag (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT UNIQUE,
category_id INT NOT NULL,
description VARCHAR(30) NOT NULL,
second_priority FLOAT(30, 30) NOT NULL
);
Second Your FKs should look like:
ALTER TABLE tag ADD CONSTRAINT FOREIGN KEY (category_id) REFERENCES category (id);
ALTER TABLE category ADD CONSTRAINT FOREIGN KEY (user_id) REFERENCES user (id);
ALTER TABLE article ADD CONSTRAINT FOREIGN KEY (user_id) REFERENCES user (id);