Prevent Duplicate Entries Composite Index - mysql

I have a Relationships table that I am trying to add a composite index to:
CREATE UNIQUE INDEX idx_relationship_userid_friend_id on Relationships(user_id, friend_id);
I would like to prevent duplicate entries for user_id and friend_id columns. i.e. Second entry of user_id = 26 and friend_id = 46 should give an error.
I ran the command above. When I run the command again, I get the following error:
Duplicate entry '36-50' for key 'idx_relationship_userid_friend_id'
When I look at the structure in for INDEXES I see the following table:
Under table info next to Create syntax, I have the following code:
CREATE TABLE `Relationships` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`friend_id` int(11) DEFAULT NULL,
`status` int(11) DEFAULT '1',
`createdAt` datetime NOT NULL,
`updatedAt` datetime NOT NULL,
`user_id` int(11) DEFAULT NULL,
`app_common` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `owner_id` (`user_id`),
KEY `app_common` (`app_common`),
CONSTRAINT `Relationships_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `Users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `Relationships_ibfk_2` FOREIGN KEY (`app_common`) REFERENCES `AppCommon` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=79 DEFAULT CHARSET=latin1;
However, I am able to insert duplicate records in the database.
Question: How can I avoid duplicate entries?

Since your table already has duplicates, it gets an error trying to create the index, so it doesn't add it.
See How to delete duplicates on a MySQL table? for how to remove duplicates. Once you've done that you can add the unique index to prevent new duplicates from being added.

Use the Unique constraint to handle your issue, something like:
CONSTRAINT UC_user_friend UNIQUE (user_id, friend_id)

Related

MySQL (Percona) Error 1452: Cannot add or update a child row for no reason

I have 2 Database Tables "users" and "tasks".
The tasks table contains two foreign keys for the createdBy_user_id and updatedBy_user_id columns both referencing users.id. If I try to insert an entry to tasks (after making sure the user referenced by the foreign keys exists) like this:
INSERT INTO tasks (createdBy_user_id,updatedBy_user_id,noOfYear,createdAt,updatedAt,status,customer_id)
VALUES (1,1,1,NOW(),NOW(),"open",1)
The query fails with Error 1452:
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test`.`tasks`, CONSTRAINT `user_id_fk_constr` FOREIGN KEY (`createdBy_user_id`) REFERENCES `users` (`id`))
I don't know why this happens, because everything works fine if I remove the constraint. The same error does not happen for the "updatedBy_user_id" column making this such confusing.
The Tables have the following DDL:
Users table:
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`active` tinyint(1) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`email_confirmed_at` datetime DEFAULT NULL,
`username` varchar(50) NOT NULL,
`password` varchar(255) NOT NULL,
`first_name` varchar(50) DEFAULT NULL,
`last_name` varchar(50) DEFAULT NULL,
`job` varchar(64) DEFAULT NULL,
`position` varchar(64) DEFAULT NULL,
`specialKnowledge` text,
`tasks` text,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
Tasks table:
CREATE TABLE `tasks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`createdAt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`noOfYear` int(11) NOT NULL,
`year` int(11) GENERATED ALWAYS AS (right(year(`createdAt`),2)) VIRTUAL NOT NULL,
`createdBy_user_id` int(11) NOT NULL,
`updatedBy_user_id` int(11) NOT NULL,
`status` enum('open','closed') NOT NULL,
`customer_id` int(11) NOT NULL,
`projectDescription` text,
PRIMARY KEY (`id`),
UNIQUE KEY `tasks_year_unique_constr` (`year`,`noOfYear`),
KEY `user_id_fk_constr` (`createdBy_user_id`),
KEY `customer_id_fk_constr` (`customer_id`),
KEY `user_up_id_fk_constr` (`updatedBy_user_id`),
CONSTRAINT `customer_id_fk_constr` FOREIGN KEY (`customer_id`) REFERENCES `Customer` (`id`),
CONSTRAINT `user_id_fk_constr` FOREIGN KEY (`createdBy_user_id`) REFERENCES `users` (`id`),
CONSTRAINT `user_up_id_fk_constr` FOREIGN KEY (`updatedBy_user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4
As you can see the datatypes match and both tables use the InnoDB Engine.
The users table contains one entry:
select id,username from users;
+----+----------+
| id | username |
+----+----------+
| 1 | admin |
+----+----------+
1 row in set (0.00 sec)
So there is no obvious reason, why the insertion should fail. Have you an idea what's wrong with my tables? Looks like a software bug.
This is a bug. MySQL 5.7 has had some troubles with generated columns and foreign keys, and I assume this is an unfixed variant of Bug #79772 Foreign key not allowed when a virtual index exists.
In your particular case, and probably depending on your exact version, any of the following modifications seems to prevent that bug from occurring:
do not use a virtual column, but make it stored
do not create a foreign key for a column directly following the virtual column, so e.g. change the column order to year, status, createdBy_user_id, updatedBy_user_id.
do not use a unique index on the virtual column, a normal index should be fine (at least in a version where the linked bug is fixed). You want a unique constraint, so this is not an option, but the fact that this fixes your problem emphasized the "bug" nature of the problem.
The second bullet point seems to be the underlying bug: I assume that some iterator doesn't count the virtual column properly, so the foreign key that shall check createdBy_user_id seems to mix up the columns and actually checks the value of year (in this case "20") against the users table. So if you have a user with id "20" in your users table, the foreign key will actually accept this, no matter what value for createdBy_user_id you are trying to insert, see the MySQL 5.7.29 fiddle.
Unless you have a specific reason to use a virtual column, using stored is probably the sane thing to do.

Simple Relation between 2 tables

I have a problem here.
I cannot add this to my db because one table is dependent of another and vice-versa.
So I get
Cannot add foreign key constraint
on the first create table that I put
How can I add this 2 tables if they both have constraints??
-- User Roles
CREATE TABLE IF NOT EXISTS `user_roles` (
`user_role_id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(45) NOT NULL,
`role` varchar(45) NOT NULL,
PRIMARY KEY (`user_role_id`),
UNIQUE KEY `uni_username_role` (`role`,`username`),
UNIQUE KEY `ix_auth_username` (`username`,`role`),
KEY `fk_username_idx` (`username`),
CONSTRAINT `fk_username` FOREIGN KEY (`username`) REFERENCES `users` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
-- Users
CREATE TABLE IF NOT EXISTS `users` (
`username` varchar(45) NOT NULL,
`name` varchar(45) DEFAULT NULL,
`hashedPassword` varchar(500) NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '1',
`image` mediumblob,
`team` int(11) DEFAULT NULL,
`userRole` int(11) DEFAULT NULL,
PRIMARY KEY (`username`),
KEY `fkteam_idx` (`team`),
KEY `fkrole_idx` (`userRole`),
CONSTRAINT `fkrole` FOREIGN KEY (`userRole`) REFERENCES `user_roles` (`user_role_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fkteam` FOREIGN KEY (`team`) REFERENCES `team` (`idteam`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
To do this, you'll need to use deferrable constraint checks, but unfortunately MySQL does not implement this standard SQL feature.
As far as I know, only Oracle and PostgreSQL support this feature (deferrable constraints). These constraints are checked at the end of the transaction, and not on every single row insertion. That would solve your problem.
Therefore, you have two options:
Switch to Oracle or PostgreSQL (unlikely, I guess) or,
Change your table definition to allow one of the foreign key constraints to accept null values.
In the second case, you would:
Insert in the table that allow null in the FK, getting the generated ID.
Insert in the other table using the ID. Then, get the second generated ID.
Update the null in first table using the second ID.
Commit.
That's it.

MySQL: Cannot add or update a child row: a foreign key constraint fails when referencing UID

I have 2 tables : tbl_usr_info which has a UID which is a primary key and auto-increment and tbl_login_info which has a LoginID (primary) and the UID along with some other information like timestamps etc.
I'm trying to reference UID from tbl_login_info with UID in tbl_usr_info by running this sql statement
CONSTRAINT `uid-info/login` FOREIGN KEY (`UID`) REFERENCES `tbl_usr_info` (`UID`)
but I'm getting this error:
Cannot add or update a child row: a foreign key constraint fails (CONSTRAINT uid-info/login FOREIGN KEY (UID) REFERENCES tbl_usr_info (UID))
tbl_usr_info table
CREATE TABLE `tbl_usr_info` (
`UID` int(50) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL DEFAULT '',
`password` varchar(50) NOT NULL DEFAULT '',
`email` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`UID`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
tbl_usr_login table
CREATE TABLE `tbl_usr_login` (
`LoginID` int(11) NOT NULL AUTO_INCREMENT,
`UID` int(50) NOT NULL,
`ip_address` varchar(55) DEFAULT NULL,
`device` varchar(100) DEFAULT NULL,
`time_stamp` datetime DEFAULT NULL,
PRIMARY KEY (`LoginID`)
)ENGINE=InnoDB DEFAULT CHARSET=latin1;
Is it the order in which I'm referencing it that's wrong?
I tested your foreign key constraint and it works without error for me. But my tables were empty.
One of the most common types of failures for a foreign key constraint is that when you create the constraint, the child table contains some values that are not present in the parent table. The foreign key constraint cannot be satisfied in that case, so creation of the constraint fails.
You can check for unmatched UID values:
SELECT COUNT(*)
FROM tbl_usr_login AS l
LEFT OUTER JOIN tbl_usr_info AS i
ON l.UID = i.UID
WHERE i.UID is NULL;
P.S.: This is tangential to your question, but I notice you're using INT(50). The argument to INT doesn't mean what you think it means. INT(50) does NOT mean you can store 50 digits. See my answer to Types in MySQL: BigInt(20) vs Int(20)
To enable foreign key, child and parent column definitions must match along with some other conditions.
In your problem case, following steps should resolve it:
create user table.
create login info table.
add index/key on the UID column in login table.
now, add referential constraint on it.
Refer to:
Mysql: Using foreign key constraints:
https://dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html

Unexpected mysql behavior when inserting new rows

I've got this query:
INSERT INTO series_measures (sensor_SID,dataset_SID,created_on)
VALUES (841468,145,'2016-10-26 11:36:07'), ... ,(841505,145,'2016-10-26 11:36:07')
ON DUPLICATE KEY UPDATE sensor_SID=VALUES(sensor_SID),dataset_SID=VALUES(dataset_SID),created_on=VALUES(created_on);
Two things that I don't understand are happening here:
A. If (sensor_SID-dataset_SID) is already inserted:
Error:
Duplicate entry '145-841468' for key 'fk_series_meas_dataset_idx'
But ON DUPLICATE KEY UPDATE should avoid that.
B. If (sensor_SID-dataset_SID) is not inserted yet:
Error:
Duplicate entry '412870' for key 'PRIMARY'
PRIMARY is SID, which has auto-increment (see create statement below), so that makes no sense to me.
This is the create statement of the table:
CREATE TABLE `series_measures` (
`SID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`sensor_SID` int(10) unsigned NOT NULL,
`dataset_SID` int(10) unsigned NOT NULL,
`created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`SID`),
UNIQUE KEY `fk_series_meas_dataset_idx` (`dataset_SID`,`sensor_SID`),
KEY `fk_series_meas_sensor_idx` (`sensor_SID`),
CONSTRAINT `fk_series_meas_dataset` FOREIGN KEY (`dataset_SID`) REFERENCES `datasets` (`SID`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_series_meas_sensor` FOREIGN KEY (`sensor_SID`) REFERENCES `sensors` (`SID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=412908 DEFAULT CHARSET=utf8;

On delete cascade not deleting the associates entries

I have a marks table which holds the foreign key to an associates message. I used ON DELETE with the expectation that deleting an entry in the marks table would also delete the associated message. It is not working as expected though. I checked out all the other post and can't find any relevant to this one.
I can insert into the database successfully
DELETE FROM marks WHERE x = 37.7836
but when I delete a mark the message that is associated with it is not deleted. Any insights as to what I am doing wrong here?
Edit:
Here is an image of me demonstrating my deletion process:
schema
DROP DATABASE uncovery;
CREATE DATABASE uncovery;
SET foreign_key_checks=0;
USE uncovery;
CREATE TABLE marks (
id int(5) AUTO_INCREMENT,
x float(10, 6),
y float(10, 6),
z float(10, 6),
timestamp timestamp DEFAULT CURRENT_TIMESTAMP,
messageId int(5),
FOREIGN KEY (messageId)
REFERENCES messages(id)
ON DELETE CASCADE,
PRIMARY KEY (id)
);
CREATE TABLE messages (
id int(5) AUTO_INCREMENT,
messageString text,
PRIMARY KEY (id)
);
EDIT 2: Schema Refactor
Here is my newly updated schema:
As you can see I am getting an error that this is invalid when I try to run it:
The foreign key as you've created it won't work the way you expect. Under your schema, if an entry is deleted from messages, it will delete all corresponding entries from the marks table, not the other way around.
Also, you must ensure that you are using the InnoDB storage engine for your tables, or your foreign keys won't work at all. They are not supported in the MyISAM engine type.
To get your schema working such that deleting a mark will cause a message to be deleted, you need to have a markId as a foreign key in the messages table, like so:
CREATE TABLE `messages` (
`id` int(5) NOT NULL auto_increment,
`messageString` text,
`markId` int(5) default NULL,
PRIMARY KEY (`id`),
KEY `markId` (`markId`),
CONSTRAINT `messages_ibfk_1` FOREIGN KEY (`markId`) REFERENCES `marks` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TABLE `marks` (
`id` int(5) NOT NULL auto_increment,
`x` float(10,6) default NULL,
`y` float(10,6) default NULL,
`z` float(10,6) default NULL,
`timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;