Foreign key constraint fails but referenced row exists - mysql

I'm running MySQL 5.7.21 on Amazon RDS.
I know this question has been asked a thousand times, but I'm getting the issue on a scenario I wouldn't expect, so please read through before downvoting or marking as duplicate.
I'm not restoring the database, just running single INSERT queries, so is not a matter of ordering.
The referenced row does exist on the table; me and my colleagues had it triple checked.
As one might expect, disabling the FK checks with SET foreign_key_checks = 0 does make the query work.
I've seen this happening because of different table charsets, but in this case, both use utf8mb4. Also both have collation set to utf8mb4_general_ci.
This is happening in a production environment, so dropping the tables and recreating them is something I would like to avoid.
Some additional information:
The FK constraint was created AFTER the original tables were already populated.
Here is the relevant portion of the current DDL:
CREATE TABLE `VehicleTickets` (
`id` varchar(50) NOT NULL,
`vehiclePlate` char(7) NOT NULL,
`organizationId` varchar(50) NOT NULL,
`createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` timestamp NULL DEFAULT NULL,
`status` varchar(15) NOT NULL DEFAULT 'OPEN',
`description` text NULL DEFAULT NULL,
`ticketInfo` json DEFAULT NULL,
`externalId` varchar(100) GENERATED ALWAYS AS (json_unquote(json_extract(`ticketInfo`,'$.externalId'))) VIRTUAL,
`value` decimal(10,2) GENERATED ALWAYS AS (json_unquote(json_extract(`ticketInfo`,'$.value'))) VIRTUAL,
`issuedAt` timestamp GENERATED ALWAYS AS (json_unquote(json_extract(`ticketInfo`,'$.issuedAt'))) VIRTUAL NOT NULL,
`expiresAt` timestamp GENERATED ALWAYS AS (json_unquote(json_extract(`ticketInfo`,'$.expiresAt'))) VIRTUAL NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `VehicleTickets_externalId_unq_idx` (`externalId`,`organizationId`),
KEY `VehicleTickets_vehiclePlate_idx` (`vehiclePlate`),
KEY `VehicleTickets_organizationId_idx` (`organizationId`),
KEY `VehicleTickets_issuedAt_idx` (`createdAt`),
KEY `VehicleTickets_expiresAt_idx` (`expiresAt`),
CONSTRAINT `VehicleTickets_Organizations_fk`
FOREIGN KEY (`organizationId`) REFERENCES `Organizations` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `Organizations` (
`id` varchar(50) NOT NULL,
`name` varchar(100) NOT NULL,
`taxPayerId` varchar(50) DEFAULT NULL,
`businessName` varchar(100) DEFAULT NULL,
`status` varchar(15) NOT NULL DEFAULT 'TESTING',
`createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`activatedAt` timestamp NULL DEFAULT NULL,
`assetConfiguration` json DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
When I run:
select * from VehicleTickets where organizationId not in (
select id from Organizations
);
I get an empty result set.
However, if I run a query like this:
insert into `VehicleTickets` (
`id`,
`createdAt`,
`organizationId`,
`ticketInfo`,
`vehiclePlate`
)
values (
'... application generated id',
'... current date ',
'cjlchoksi01r8nfks3f51kht8', -- DOES EXIST on Organizations
'{ ... some JSON payload }',
'... vehicle plate'
)
This produces the following error:
Cannot add or update a child row: a foreign key constraint fails
(VehicleTickets, CONSTRAINT VehicleTickets_Organizations_fk
FOREIGN KEY (organizationId) REFERENCES Organizations (id))
Additionally, it gives me:
"errno": 1452,
"sqlState": "23000",
I've read through several threads regarding this issue, but couldn't find a similar case.

Related

Is it possible to have a 1 to 0 or 1 recursive relationship and how to query such table?

I have this Ticket table supposed to be used in a Queue system where I can forward a ticket from a Service queue to a different queue.
My Ticket table has fields such as number, ticketRequest (timestamp of when the ticket was created), expectedCallTimestamp (timestamp of predicted call to be attended), etc...
Here's the CREATE SCRIPT for the table Ticket:
CREATE TABLE `ticket` (
`ticket_id` int(11) NOT NULL AUTO_INCREMENT,
`wrong_ticket_id` int(11) DEFAULT NULL,
`number` int(3) NOT NULL,
`forwarded` tinyint(1) NOT NULL DEFAULT '0',
`answered` tinyint(1) DEFAULT NULL,
`ticketRequest` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`expectedCallTimestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`callTimestamp` timestamp NULL DEFAULT NULL,
`serviceDuration` time DEFAULT NULL,
`service_id` int(11) NOT NULL,
`organic_unit_id` int(11) NOT NULL,
`device_id` int(11) NOT NULL,
`user_id` int(11) DEFAULT NULL,
`counter_id` int(11) DEFAULT NULL,
PRIMARY KEY (`ticket_id`),
UNIQUE KEY `UQ_ticket_wrongTicketId` (`wrong_ticket_id`) USING BTREE,
KEY `FK_ticket_queue` (`service_id`,`organic_unit_id`),
KEY `FK_ticket_device` (`device_id`),
KEY `FK_ticket_workerbycounter` (`user_id`,`counter_id`),
CONSTRAINT `FK_ticket_device` FOREIGN KEY (`device_id`) REFERENCES `device` (`device_id`),
CONSTRAINT `FK_ticket_queue` FOREIGN KEY (`service_id`, `organic_unit_id`) REFERENCES `queue` (`service_id`, `organic_unit_id`),
CONSTRAINT `FK_ticket_workerbycounter` FOREIGN KEY (`user_id`, `counter_id`) REFERENCES `workerbycounter` (`user_id`, `counter_id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=latin1
OK, so what I thought was - when a client picks a different service that he intended to pick (it happens sometimes) I, as the operator behind the counter, can create a new ticket with the same ticketRequest timestamp of the wrongServiceTicket and use that wrongServiceTicket's.ticket_id to reference it through a forwardedTicket's.wrong_ticket_id (the one that has the intended/correct service) for several purposes.
My approach to this is having a 1 to 0 or 1 recursive relationship on the table Ticket, with a nullable & unique field of wrong_ticket_id that is the same as the wrongServiceTicket.ticket_id when a forwardedTicket is created, as previously explained.
Here's a data model example of what I'm trying to build:
Is it possible to have this implementation or is there a better way to handle this problem? And how do I select the info of the wrongServiceTicket when, for example, calling the next ticket in line when having a ticket that's forwarded in the same queue as other tickets that aren't?
As I understood you intend to establish parent-child relationship between existing ticket (wrong one) and new ticket (corrected one).
Your suggested approach is viable except few minor corrections -
Keep wrong_ticket_id column as NULLABLE as it can be null in most of the tickets which were created with a valid service.
You can optionally put a check constraint on wrong_ticket_id. This column will be populated only when 'forwarded' = True.

MySQL Workbench wont make constraint when parent table has Generated Virtual columns

SETUP
MySQL Workbench (ver 6.3.9)
MySQL 5.7.21
My setup is simple.. I have 2 tables:
CREATE TABLE `UserDevices` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`UserID` int(11) DEFAULT NULL,
`UUID` binary(16) DEFAULT NULL,
`DeviceName` varchar(45) DEFAULT NULL,
`DeviceType` tinyint(3) NOT NULL DEFAULT '1',
`CreatedDate` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`TimeStamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `UserInfo` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`UUID` binary(16) DEFAULT NULL,
`UUIDText` varchar(40) GENERATED ALWAYS AS (insert(insert(insert(insert(hex(`UUID`),9,0,'-'),14,0,'-'),19,0,'-'),24,0,'-')) VIRTUAL,
`FirstName` varchar(45) DEFAULT NULL,
`LastName` varchar(45) DEFAULT NULL,
`FullName` varchar(90) GENERATED ALWAYS AS (concat(`FirstName`,' ',`LastName`)) VIRTUAL,
`Email` varchar(120) DEFAULT NULL,
`Status` tinyint(3) DEFAULT '0',
`AccountType` tinyint(3) DEFAULT '1',
`CreatedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`TimeStamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
PROBLEM
When working inside Workbench I'm simply trying to Make a Foreign key constraint in table "UserDevices" on Column "UserID" Pointed at Table "UserInfo" Column "ID". When selecting "UserInfo" as the Referenced Table. I can not put a check next to UserID.. Also no columns show up in the drop down list under Referenced Column..
QUESTION
I understand there are a number of reasons this scenario would happen. But I'm not seeing Any data type mismatch or such that would explain this. What is making it so I can't select UserID.ID?
P.S. Setting up another table named "DeviceMeasurements" with a Column "DeviceID" I'm completely successful at setting up the constraint exactly as expected.
UPDATE
On a hunch since this is my first time playing around with Generated Virtual Columns. I went into the table and removed columns "UUIDText" and "FullName". NOW I can build my constraints as desired. But my question stands. Why can't I build constraint with the tables built as above!?
UPDATE 2
This has been confirmed as a bug in WorkBench. Manually adding the constraint via SQL code is a valid work around currently. Please see accepted answer.
Can confirm, this is a bug in WB. Have raised it with MySQL dev team.
Bug link

How to debug mysql foreign key constraint, ON DELETE CASCADE not deleting rows from Child table on production environment

I have defined 2 tables and a foriegn key constraint between them as follows:
| users | CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`account_master_id` int(11) NOT NULL,
`user_type_id` int(11) NOT NULL,
`user_group_id` int(11) NOT NULL,
`user_type_code` char(1) NOT NULL,
`membership_number` varchar(40) NOT NULL,
`password` varchar(60) NOT NULL,
`email` varchar(200) NOT NULL,
`isd` varchar(10) NOT NULL,
`mobile` varchar(20) NOT NULL,
`passenger_id` int(11) NOT NULL,
`added_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`added_by` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
KEY `account_master_id` (`account_master_id`),
CONSTRAINT `acMaster_to_user` FOREIGN KEY (`account_master_id`) REFERENCES `account_master` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=189 DEFAULT CHARSET=utf8 |
user_oauth | CREATE TABLE `user_oauth` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`service` varchar(30) NOT NULL,
`auth_id` varchar(100) NOT NULL,
`email` varchar(100) NOT NULL,
`auto_share` tinyint(4) NOT NULL,
`photo` varchar(255) NOT NULL,
`auth_token_short` varchar(255) DEFAULT NULL,
`auth_details` text NOT NULL,
`device_type` varchar(60) NOT NULL,
`login_date` datetime NOT NULL,
`login_ip` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `user` (`user_id`),
CONSTRAINT `user_to_oauth` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=latin1 |
on deleting a row from users table, deletes corresponding entries from user_oauth table in LOCAL and Staging environment. but however same thing is not working in PRODUCTION environment. I want to know how can i debug this.
update:
both tables are innodb
running mysql in strict mode
The reasons why foreign keys might not work are:
they are not supported (e.g. on MyISAM-tables)
they are disabled
MySQL allows you to disable foreign key contraints by setting the system variable foreign_key_checks to 0. This will allow you to violate all foreign key constraints (e.g. delete a parent or add parentless childs). But it will in turn also disable related features like cascades, which are there to automatically prevent specific constraint violations (by e.g. deleting the children) - which of course won't occur anymore if the option is disabled.
The idea is to help you with some administration tasks, e.g. importing data when the referenced data is not yet there, but should usually not be used during normal operation. If the setting reappears, you might want to check your apps if one is setting this option by accident, as it is enabled after every server start by default and has to be disabled explicitely.
You can check the current setting by e.g. using
select ##foreign_key_checks;
You can use
SET foreign_key_checks = 1;
to enable it again, but be aware that it will not check your current data:
Setting foreign_key_checks to 1 does not trigger a scan of the existing table data. Therefore, rows added to the table while foreign_key_checks=0 will not be verified for consistency.
So you will have to check and fix it yourself. You can do it either before or after you enabled the setting again, although it might be easier to do it before. To trigger a recheck, you can and should drop and recreate the foreign key, just to make sure everything is consistent now.

Mysql Percona crushes the table while trying to create a unique index on fields where there is a generated one

CREATE TABLE tasks (
`user_id` INT(10) UNSIGNED NOT NULL,
`name` VARCHAR(255) NOT NULL,
`code` VARCHAR(255) NOT NULL,
`params` JSON,
`hash` VARCHAR(32) GENERATED ALWAYS AS (MD5(`params`)),
`created_at` TIMESTAMP NULL DEFAULT NULL,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
CONSTRAINT `tasks_user_id_users_id_fk`
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
then i try to add a unique index on user_id and hash, i tried to do it during the table create, but there were a problem with the foreign key, so i decided to do it separately.
ALTER TABLE tasks
ADD UNIQUE INDEX `tasks_user_id_hash_unique_idx` (`user_id`, `hash`);
Then i get strange problem error 1146
Table '<dbname>.tasks' doesn't exist
Since then strange errors
1. I cannot delete it while it exists and i cannot do with it anything, cause it does not exist. After mysql reload I get the table deleted but if i try to create it again:
Error Code: 1813
Tablespace '<dbname>.tasks' exists.
I found the decision for unique field, i simply genereate md5( hash + user_id) UNIQUE. But How can I get rid of that problem which still exists and what is going on. It looks like a BUG in mysql Percona 5.7.14-7
so the right way to create the table
CREATE TABLE tasks (
`user_id` INT(10) UNSIGNED NOT NULL,
`name` VARCHAR(255) NOT NULL,
`code` VARCHAR(255) NOT NULL,
`params` JSON,
`hash` VARCHAR(32) GENERATED ALWAYS AS (MD5(CONCAT(`params`,`user_id`))) UNIQUE,
`created_at` TIMESTAMP NULL DEFAULT NULL,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
CONSTRAINT `tasks_user_id_users_id_fk`
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
But, again, the question with the ghosts still exists after the first tries!

Creating composite primarykey using a foreignkey and a non key attribute

This is my auto generated code after creating the batch table. while inserting data to this table
BatchID=1,Course_CourseID=1
BatchID=1,Course_CourseID=2
it is creating an error saying "Duplicate entry '1' for key 'BatchID_UNIQUE'".
I'm using C# 2010 express windows application as well as MySQl 5.1
My table schema is here
CREATE TABLE `batch` (
`BatchID` int(11) NOT NULL,
`Course_CourseID` int(11) NOT NULL,
`NoOfStudents` int(11) DEFAULT NULL,
`ClassRoom` varchar(45) DEFAULT NULL,
`StartDate` varchar(45) DEFAULT NULL,
`Day` varchar(45) DEFAULT NULL,
`Time` varchar(45) DEFAULT NULL,
PRIMARY KEY (`BatchID`,`Course_CourseID`),
UNIQUE KEY `BatchID_UNIQUE` (`BatchID`),
KEY `fk_Batch_Course1` (`Course_CourseID`),
CONSTRAINT `fk_Batch_Course1` FOREIGN KEY (`Course_CourseID`)
REFERENCES `course` (`CourseID`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Well, the error message quite clearly refers to this string:
UNIQUE KEY `BatchID_UNIQUE` (`BatchID`)
So what you have to do is either drop this index (with...
ALTER TABLE `batch` DROP INDEX `BatchID_UNIQUE`
... command, or just exclude this line from the table's definition (in CREATE TABLE).
All that said assuming that you really don't need your batch ids to be unique (in other words, there's no logical error in your INSERT statement. That seems to be the case, though: pair BatchID-Course_CourseID is already defined as unique (via PRIMARY KEY).
Try it this way. Drop your batch table and then run this sql. Rightly answered that you can not have two same vaues for a unique key. So I removed the unique key line as well.
CREATE TABLE IF NOT EXISTS `batch` (
`BatchID` int(11) NOT NULL,
`Course_CourseID` int(11) NOT NULL,
`NoOfStudents` int(11) DEFAULT NULL,
`ClassRoom` varchar(45) DEFAULT NULL,
`StartDate` varchar(45) DEFAULT NULL,
`Day` varchar(45) DEFAULT NULL,
`Time` varchar(45) DEFAULT NULL,
PRIMARY KEY (`BatchID`,`Course_CourseID`),
KEY `Course_CourseID` (`Course_CourseID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `batch` ADD CONSTRAINT `batch_ibfk_2` FOREIGN KEY (`Course_CourseID`)
REFERENCES `course` (`CourseId`) ON DELETE NO ACTION ON UPDATE NO ACTION;