First experience of Foreign keys, what've I done wrong? - mysql

I've been hinted into giving foreign keys a go as I'm trying to get better at database programming. The problem was, if a job in the jobs table was deleted, it left orphan messages relating to it in the messages table. So foreign keys were introduced to me to be the solution.
However, I've gotten this when trying to do it, and I'm not sure what it's saying, plus, I'm not even sure which way round I should be setting the restriction, from the job to the messages, or vice versa. :(
Here's the error:
#1452 - Cannot add or update a child row: a foreign key constraint fails (`nzua9c8_tasks`.<result 2 when explaining filename '#sql-2929_701930'>, CONSTRAINT `#sql-2929_701930_ibfk_1` FOREIGN KEY (`id`) REFERENCES `jobs` (`id`) ON UPDATE NO ACTION)
Also, the interface for PHPMyAdmin suggested that when I delete a task, the message ID would be "restricted". I didn't have an option for delete. I want any messages with a "job_id" that matches an id in the jobs table to be deleted too.
Thanks for any help offered.

You want to add on delete cascade to your foreign key definition (just after on update no action in your script).
So it would be something like:
alter table messages add foreign key (id) references jobs (id) on delete cascade
More can be found here.

If one job contains several messages (1:N relation) then message table should have foreign key to the jobs table.
Your error message basically says that you cant update/delete row when there is something referencing it by foreign constraint.
So if table jobs have records:
id name
1 job1
2 job2
And table of messages have records
id name job_id
1 mes1 1
2 mes2 2
Then you cant just drop record from first table. You should at first update referencing record in message table.
Also, you can change foreign key policy in such way that dependant records will be destroyed/updated automatically.

Foreign keys with corresponding "constraints" are for ensuring "referential integrity" and will solve the "dangling reference" problem you mentioned.
The "downside" to foreign keys is that the data must be populated into the database in the correct order:
If the rows in table "A" contain a foreign key pointer to one or more rows in table "B" then the rows in table "B" must be created before the rows in Table "A" can be created - otherwise the constraint will throw an error.

Related

SQL: Cannot delete or update parent row: a foreign key constraint fails

Whenever I try to delete a survey from table "survey" like this:
DELETE FROM surveys WHERE survey_id = 77
It prompts me an error stated below:
#1451 - Cannot delete or update a parent row: a foreign key constraint fails ('user_surveys_archive', CONSTRAINT
'user_surveys_archive_ibfk_6' FOREIGN KEY ('user_access_level_id')
REFERENCES 'user_surveys' ('user_access_level_id') ON DELETE NO ACTION
)
First thing: I do not have any such table with this name "user_surveys_archive_ibfk_6"
2nd thing: There is no record of this survey in other tables.
Any idea on how can I delete this record of fix this issue?
Edit
This is the line I found when I export the table Constraints for table surveys
ALTER TABLE `surveys`
ADD CONSTRAINT `surveys_ibfk_1` FOREIGN KEY (`survey_type_id`) REFERENCES `survey_types` (`survey_type_id`) ON DELETE CASCADE ON UPDATE NO ACTION;`
You will need to first remove or update some rows in table user_surveys_archive.
Those rows are related to rows in the user_surveys table.
Likely, there's a foreign key constraint defined on table user_surveys that references rows in surveys you are attempting to delete.
(You'd need to check the foreign key definition, quickest way to get that is a
SHOW CREATE TABLE user_surveys
And look for REFERENCES surveys. (Likely, its a column named survey_id, but we're just guessing without looking at the definitions of the foreign key constraints.)
To find the rows in user_surveys_archive that are preventing the DELETE from happening...
SELECT a.*
FROM user_surveys_archvive a
JOIN user_surveys u
ON u.user_access_level_id = a.user_access_level_id
JOIN surveys s
ON s.survey_id = u.survey_id -- change this to whatever the FK is
WHERE s.survey_id = 77
It's likely that the foreign key constraint from user_surveys to surveys is defined with ON DELETE CASCADE. The attempt to delete rows from surveys identifies rows in user_surveys that should automatically be removed.
The attempt to automatically remove the rows from user_surveys is what's violating the foreign key constraint defined in user_surveys_archive. And that foreign key is not defined with ON DELETE CASCADE.
(The other possibility is that there's a trigger defined that's doing some DML operations, but that would be odd.)
Once you identify the rows, you need to decide what changes to make to allow you to remove rows from surveys.
You can either DELETE the rows, or UPDATE them.
To delete the rows from user_surveys_archive, modify the query above and replace SELECT with DELETE. If the user_access_level_id column in user_surveys_archive allows for NULL values, you can do an update.
Replace SELECT a.* FROM with UPDATE, and add SET a.user_access_level_id = NULL on a line above the WHERE clause...
UPDATE user_surveys_archvive a
JOIN user_surveys u
ON u.user_access_level_id = a.user_access_level_id
JOIN surveys s
ON s.survey_id = u.survey_id -- change this to whatever the FK is
SET a.user_access_level_id = NULL
WHERE s.survey_id = 77
(It seems strange to me that name of the foreign key column is user_access_level_id. But its just a column name, it could be named anything... seems odd to me because of the conventions and patterns that we follow in naming foreign key columns.)
You should allow the foreign key to be NULL and then choose ON DELETE SET NULL.
Personally I would recommend using both "ON UPDATE CASCADE" as well as "ON DELETE SET NULL" to avoid unnecessary complications, however your setup may dictate a different approach.
Hope this helps.
The survey_id of 77 must be referenced in a table possibly named user_surveys_archive. The error is saying the name of the foreign key restraint which is the user_surveys_archive_ibfk_6. If you need to delete survey_id = 77 you will have to delete any child records or other records that reference it as a foreign key.
Edit After posting my answer I saw the comments above. atif the foreign key in the user_surveys_archive table may be named differently but still equal the value of 77 that you are trying to delete, and/or be referencing the survey_id. If that is not the case then there are some other problems within the database. You could try and look at the code for the FK to see how it was made and what fields it is referencing, or run a query to see if there are any records in the user_surveys_archive where the user_access_level_id is = 77.
Edit 2 spencer7593's answer lays out how to run some of the queries mentioned in my answer.

Remove table entry if BOTH foreign keys were deleted

I have a table with 2 foreign keys that reference the same field within another table. I know how to define the foreign key constraint to remove my table entry if at least one of the two foreign keys become deleted. But instead I want to keep the table entry if at least one of the foreign keys still exist?
CREATE TABLE PrivateMessages
...
INDEX(FromEmail, ToEmail),
FOREIGN KEY(FromEmail, ToEmail)
REFERENCES Users(Email, Email)
ON UPDATE CASCADE
ON DELETE CASCADE,
...
The table stores messages between two users. I want to delete messages if both users don't exist any longer, only. Maybe, is there a better approach to realize this?
... I want to keep the table entry if at least one of the foreign keys still exist
I want to delete messages if both users don't exist any longer, only.
It can't be achieved just by defining a constraint.
Possible procedure:
Define a marked_for_delete bit not null default 0 column feature.
Whenever you want to delete a user, mark it for deletion, by an
update call.
Define either a before or an after type of delete trigger on
the table.
In the trigger body, check if the combo of fromEmail and toEmail
are marked for deletion, then execute a delete statement on the
parent table and the child combo will be deleted as your required
condition matched.

Parent table cannot be updated when foreign key is present

There are two tables one is a parent i.e., groups table which has foreign key to a child table i.e., users. I am not able to edit foreign key column in parent table where as I have given it to cascade to child table. It gives a error as follows:
Error Code : 1452
Cannot add or update a child row: a foreign key constraint fails (`tms`.`groups`, CONSTRAINT `FK_groups` FOREIGN KEY (`GroupName`) REFERENCES `users` (`groupname`) ON DELETE CASCADE ON UPDATE CASCADE)
Thanks,
-Jeevan
I assume a group contains many users, and a users belongs to one group.
Then you have declared the foreign key in the wrong direction. Actually users.groupname must reference tms.groups. Drop the current foreign key and rebuild it the other way around (in the users table).
This happens if you try to reference a non existing entry in database. In short, you inserted into groups and tried to reference an user entry which doesn't exist yet.

MySQL Error, what does it mean

I am trying to update two columns in a xref database. I am getting this error message:
Cannot add or update a child row: a foreign key constraint fails (`globaldetroit`.`org_cult_xref`, CONSTRAINT `org_cult_xref_ibfk_1` FOREIGN KEY (`org_id`) REFERENCES `organization` (`org_id`) ON DELETE CASCADE ON UPDATE CASCADE)
I want to be able to have a many-many relationship, and these errors seem to prevent me from having one.
EDIT:
That is very odd! There most certainly is a column org_id with the value of "6" (as an integer) in the table organization! I just checked!
You are trying to set a value that has a foreign key constrant -- ie the key does not exist in the foreign table.
So globaldetroit's org_cult_xref references an org_id in organization that does not exist.
you're putting in field globaldetroit.org_cult_xref value not existing in organization.org_id
Many-many relationship is bad, don't go there.
Your error seems to be caused because the row you insert in org_cult_xref has a column org_id and the value you insert there cannot be found in the organization table.
You have a foreign key defined on the table you are trying to insert/update into, which basically says the value in org_id should exist in the organization table, and it is not the case.

mysql delete and foreign key constraint

I'm deleting selected rows from both table in MYSQL, the two tables have foreign keys.
DELETE d,b
FROM A as b
INNER JOIN B as d on b.bid=d.bid WHERE b.name LIKE '%xxxx%';
MYSQL complains about foreign keys even though I'm trying to delete from both tables:
Error: Cannot delete or update a parent row: a foreign key constraint
fails (`yyy/d`, CONSTRAINT `fk_d_bid` FOREIGN KEY (`bid`) REFERENCES
`b` (`bid`) ON DELETE NO ACTION ON UPDATE NO ACTION)
what's the best solution here to delete from both table?
Change this constraint to use ON DELETE CASCADE -- which means that if a row is deleted, then any "child" rows will be automatically deleted as well.
Of course take good care of using CASCADE -- only use it when necessary. If you're overzealous with it, and accidentally do a well-placed DELETE, it might end up deleting half of your database. :)
See documentation on foreign key constraints.
I think I see what you're trying to do
If you can't change the table structure, then you could use 2 statements, the first with a sub-select
delete from B where bid IN (select bid from A where name like '%xxxx%');
delete from A where name like '%xxxx%';