How to prevent adding duplicates to many-to-many table - mysql

I have a many-to-many table with UserId and TaskId
When I add data to the table I can add the same UserId and TaskId more than once.
I use the following query to add the data:
Create table if not exists user_task(UserId INT, FOREIGN KEY (UserId)
REFERENCES USERS(id), TaskId INT, FOREIGN KEY (TaskId) REFERENCES TASKS(id));
How can I prevent adding duplicates of those in the table?
+-----------------+
| UserId | TaskId |
+-----------------+
| 1 | 1 |
| 2 | 1 |
| 1 | 1 |
+---------+-------+
I know I can put DISTINCT in the SELECT query to get the data without duplicates but it doesn't prevent from being added to the table.

You can use Unique Index Constraint
Basically it will check for duplicates (upon one column or groups of columns) and stops insert/update operations if constraint would break.
If you're interested in how to deal with this, check out the manual

Related

How to design a simple and efficient MySQL blacklist table

Suppose I have user table my_users in which there is a primary key id. Also, I wish to design (in MySQL) a simple blacklist table, whose declaration looks like this:
CREATE TABLE IF NOT EXISTS black_list (
user_id INT NOT NULL,
bad_string VARCHAR(100) NOT NULL,
FOREIGN KEY (user_id) REFERENCES my_users(id),
PRIMARY KEY (user_id, bad_string));
The interpretation of any row in the black_list is that a user with the ID user_id wants to blacklist the string bad_string. Obviously, user_id cannot be unique since a single user may have more than one blacklisted string. Other way around, bad_string cannot be unique since more than one users may have blacklisted the same string. However, the pair (user_id, bad_string) should be unique since it makes no sense for the user to black list the same string more than once.
When we select a black list via a user ID (SELECT * FROM black_list WHERE user_id = X) in the worst case, MySQL will have to scan the entire black_list table.
My question here is: is there a way for running the above SELECT statement in sublinear time with regard to the number of rows in the black_list table? If yes, how can I accomplish that?
Your assertion that SELECT * FROM black_list WHERE user_id = X will have to scan the entire black_list table is incorrect.
In this sql fiddle, you can see it's using an index:
+----+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | black_list | ref | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | Using index |
+----+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+

MySQL add dynamic amount to an table

i am very new to MySQL and making my first steps with it. I have right now 2 tables that look like this:
Event:
+-------+-------------+
| Field | Type |
+-------+-------------+
| id | int(11) |
| name | varchar(30) |
| date | datetime |
+-------+-------------+
User:
+----------+-------------+
| Field | Type |
+----------+-------------+
| name | varchar(30) |
| amount | int |
+----------+-------------+
Now i would like to be able to add an dynamic amount of Users to an Event but i am not sure how i should create an dynamic table for that or how otherwise i should implement such an behaviour. What would be the best practice?
Help would be appreciated.
That is a classic many-to-many relation. You handle it by adding a new table
event_users table
-----------------
event_id
user_id
For an event (id=1) having two users (id=3,4) it looks like this
event_id | user_id
1 | 3
1 | 4
To get all users of an event you would do this
select u.name
from users u
join event_users eu on eu.user_id = u.id
join events e on eu.event_id = e.id
where e.name = 'my event'
You need a third table, a junction table:
create table UserEvents (
UserEventId int auto_increment primary key,
UserId int,
EventId int,
constraint fk_userevents_userid foreign key (UserId) references Users(id),
constraint fk_userevents_eventid foreign key (EventId) references Events(id)
);
Notes:
I name tables in the plural.
The id for the table is the singular followed by "id" (so I prefer Users(UserId) and Events(EventId).
This has declared foreign key relationships.
If duplicates are not allowed, then you also want a unique constraint on (UserId, EventId).
For this you should use a third table in which you should maintain event and related user id.
user_event
id user_id event_id created_date
user_id and event_id are foreign keys for this table and will point primary keys of user and event table respectively

The Foreign Key Options, ON DELETE CASCADE not working?

I am trying to use ON DELETE CASCADE for a database I'm working on. Didn't seem to work so I tested it out on a simple example with no success.
CREATE TABLE foo (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
data VARCHAR(10),
PRIMARY KEY (id)
)ENGINE=InnoDB;
CREATE TABLE foo2 (
id INT UNSIGNED NOT NULL,
data2 VARCHAR(10),
PRIMARY KEY (id),
CONSTRAINT fk_foo2_id FOREIGN KEY (id) REFERENCES foo(id) ON DELETE CASCADE
)ENGINE=InnoDB;
INSERT INTO foo (data) VALUE ('hello'),('world'),('mysql');
INSERT INTO foo2 (data2) VALUE ('hello2'),('world2'),('mysql2');
SELECT * FROM foo;
+----+-------+
| id | data |
+----+-------+
| 1 | hello |
| 2 | world |
| 3 | mysql |
+----+-------+
3 rows in set (0.00 sec)
SELECT * FROM foo2;
+----+--------+
| id | data2 |
+----+--------+
| 1 | hello2 |
| 2 | world2 |
| 3 | mysql2 |
+----+--------+
3 rows in set (0.00 sec)
DELETE FROM foo WHERE id=2;
SELECT * FROM foo;
+----+-------+
| id | data |
+----+-------+
| 1 | hello |
| 3 | mysql |
+----+-------+
2 rows in set (0.00 sec)
SELECT * FROM foo2;
+----+--------+
| id | data2 |
+----+--------+
| 1 | hello2 |
| 2 | world2 |
| 3 | mysql2 |
+----+--------+
3 rows in set (0.00 sec)
I can't for the life of me figure out why this isn't working. I looked at similar questions and answers on here and I did exactly what they said and it still didn't work. Most of them just said to change to ENGINE=InnoDb, but I tried it and no success.
There must be something I'm missing here, and it's probably very obvious.. Monday mornings.
If anyone can shed some light on this little noob problem of mine, I would greatly appreciate it!
Edit: removed the auto_increment from id in foo2 as it did not belong there
The first thing that pops to mind is to check the setting of the foreign_key_checks variable. If that's set to 0 (FALSE), then foreign key constraints are NOT enforced.
SHOW VARIABLES LIKE 'foreign_key_checks'
To enable foeign key constraints, set to the variable to 1
SET foreign_key_checks = 1;
NOTE: this affects only the current session. New sessions inherit the GLOBAL setting.
Also, verify that your tables are actually using the InnoDB engine, and that the foreign keys are defined. Easiest way is to get the output from:
SHOW CREATE TABLE foo;
SHOW CREATE TABLE foo2;
FOLLOWUP
This is something that we expect NOT to be broken in MySQL 5.1.61.
As a workaround, try defining the foreign key constraint as a separate ALTER TABLE statement.
ALTER TABLE foo2
ADD CONSTRAINT fk_foo2_id FOREIGN KEY (id) REFERENCES foo(id) ON DELETE CASCADE ;
I don't see much use in a foreign key constraint between two columns that are both defined with "auto_increment". In your example, you could easily create several rows in table "foo" (without a counterpart in "foo2"), and from then onwards you could not control whether "id" values in both tables match.
I admit I didn't check the documentation, but it would not surprise me if MySQL silently ignored a foreign key constraint for an auto-generated column.
IMNSHO, your table "foo2" should use "id" values which are set explicitly and reference specific rows in "foo", because then it would make sense that deleting such "foo" rows should cascade onto "foo2".

SQL - How to prevent duplicate in specific condition?

In general, we can use unique key or primary key to prevent this, but in my case. I am creating an MyFavorite table like this:
---------------------------------------------------------
| UserName | FavoriteLink |
---------------------------------------------------------
| Ryan | http://www.google.com |
---------------------------------------------------------
| Ryan | http://www.yahoo.com |
---------------------------------------------------------
| Joyce | http://www.google.com |
---------------------------------------------------------
| Joyce | http://www.cnn.com |
---------------------------------------------------------
So, each user can have a lot of favoritelinks, but they shouldn't have duplicate favoritelink, for example, Ryan shouldn't have two favoritelink for http://www.google.com. but for this table, FavoriteLink field may be duplicate, because both Ryan and Joyce, they all have favoritelink for http://www.google.com.
Here is the question: how can I insert data into this table without duplicate FavoriteLink for specific person?
Composite keys.
CREATE TABLE userlinks (
user VARCHAR(255),
link VARCHAR(255),
PRIMARY KEY (user, link)
)
or
CREATE TABLE userlinks (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
user VARCHAR(255),
link VARCHAR(255),
UNIQUE KEY (user, link)
)
depending on what exactly it is you want.
You can add a composite unique index.
CREATE UNIQUE INDEX username_favorite_uniq ON yourTable (UserName, FavoriteLink)

Should I make a FK relation on which table?

I have two tables like:
-- users
+----+--------+---------------------+----------+--------------------+
| id | name | email | password | cookie |
+----+--------+---------------------+----------+--------------------+
-- user_detail
+---------+-----+-------------------+----------------+
| user_id | age | about | birthday |
+---------+-----+-------------------+----------------+
I need to make a relation between users(id) and user_detail(user_id) columns. Now I want to know, should I open user_detail table and create a relation on the user_id column of it (which refers to users(id)) or vice versa?
You are looking for syntax like this:
alter table user_details add constraint fk_user_details_user_id
foreign key (user_id) references users(id);