mysql circular dependency in foreign key constraints - mysql

Given the schema:
What I need is having every user_identities.belongs_to reference an users.id.
At the same time, every users has a primary_identity as shown in the picture.
However when I try to add this reference with ON DELETE NO ACTION ON UPDATE NO ACTION, MySQL says
#1452 - Cannot add or update a child row: a foreign key constraint fails (yap.#sql-a3b_1bf, CONSTRAINT #sql-a3b_1bf_ibfk_1 FOREIGN KEY (belongs_to) REFERENCES users (id) ON DELETE NO ACTION ON UPDATE NO ACTION)
I suspect this is due to the circular dependency, but how could I solve it (and maintain referential integrity)?

The only way to solve this (at least with the limited capabilities of MySQL) to allow NULL values in both FK columns. Creating a new user with a primary identity would then look something like this:
insert into users (id, primary_identity)
values (1, null);
insert into identities (id, name, belongs_to)
values (1, 'foobar', 1);
update users
set primary_identity = 1
where id = 1;
commit;
The only drawback of this solution is that you cannot force that a user has a primary identity (because the column needs to be nullable).
Another option would be to change to a DBMS that supports deferred constraints, then you can just insert the two rows and the constraint will only be checked at commit time. Or use a DBMS where you can have a partial index, then you could use the solution with an is_primary column

I would not implement it this way.
Remove the field primary_identity from table users, and the add an additional field to table user_profiles called is_primary, and use this rather as the indicator of a primary profile

This will prevent having NULLs for FKs, but still does not enforce for primary profile to exists -- that has to be managed by application.
Note the alternate key (unique index) {UserID, ProfileID} on Profile table and matching FK on PrimaryProfile.

The problem seems to be that you are trying to keep the primary identity information in the user_identities table.
Instead, I suggest you put the primary user info (name/email) into the users table. Do not foreign key to the user_identities table.
Only foreign key from the user_identities table
All constraints will now work ok as they are only one way.
user_identities cannot be entered unless the primary user (in table users) is present. Similarly the primary user should not be deletable where there are existing child identities (in user_identities).
You might want to change the name of the tables to "primary_users" and "secondary_users" to make it obvious what is going on.
Does that sound okay?

This question was raised at How to drop tables with cyclic foreign keys in MySQL from the delete side of things, but I think that one of the answers is applicable here as well:
SET foreign_key_checks = 0;
INSERT <user>
INSERT <user identity>
SET foreign_key_checks = 1;
Make that a transaction and commit it all at once. I haven't tried it, but it works for deletes, so I don't know why it wouldn't work for inserts.

I've not used it, but you could try INSERT IGNORE. I'd do the two of those, one for each table, such that once they are both done, referential integrity is maintaing. If you do them in a transaction, you can roll back if there is a problem inserting the second one.
Since you're ignoring constraints with this feature, you should do that check in program code instead, otherwise you may end up with data in your database that ignores your constraints.
Thanks to #Mihai for pointing out the problem with the above. Another approach would be to disable constraints whilst you do inserts, and re-enable them afterwards. However, on a large table that might produce more overhead than is acceptable - try it?

Related

Preventing duplicate entries during signing up

There is a checking process during signing up so that an existing username and e-mail can’t be used with a new registration. After validation, the data is stored immediately and we can’t see what causes the problem. Maybe we should use some type of locking?
We use a unique hash in the form, which we swap after every process, somehow it still occurs that a user registration is stored twice because of double clicking or other event.
Do you have any ideas how we could prevent double entries?
Thank you.
If you want values to be unique in a table, then use a unique constraint in the table:
alter table t
add constraint unq_username_email unique (username, email);
Let the database do the work, so it guarantees data integrity. Then you don't have to worry about race conditions.
I should note: You might want two unique constraints. It is unclear exactly what you want to be unique:
alter table t add constraint unq_username unique (username);
alter table t add constraint unq_email unique (email);

Opposite of RESTRICT in MySQL Foreign Key On Delete?

I'm in the process of redesigning some application security log tables (things like when users log in, access different files, etc.) to address some changing requirements. They were originally made with MyISAM, but don't really get accessed that often and switching to InnoDB and adding a bunch of foreign keys for data integrity would really be more beneficial. Since I have to remake the tables anyway, I figure this is as good a time as ever to make the switch.
For the most part, everything is straightforward foreign keys and works as expected. The only part that where I'm trying something weird and hitting problems is with user_ids. Each record in these log tables is associated with a user_id, and I want to make sure the given user_id exists when a record is inserted. Adding a foreign key that references the user table solves that problem - simple stuff. Here are some concise, representative tables:
The User Table
CREATE TABLE tbl_user (
id INT(10) NOT NULL AUTO_INCREMENT,
first_name VARCHAR(50),
PRIMARY KEY(id)
) ENGINE=InnoDB;
Example Log Table
CREATE TABLE tbl_login_time (
id INT(10) NOT NULL AUTO_INCREMENT,
user_id INT(10) NOT NULL,
login_at TIMESTAMP NOT NULL,
PRIMARY KEY(id),
CONSTRAINT 'tbl_login_time_fk_1` FOREIGN KEY (user_id) REFERENCES tbl_user
ON UPDATE CASCADE ON DELETE ???
) ENGINE=InnoDB;
My problem is that I want the foreign key enforced for inserts, updates to be cascaded, but deleting records in tbl_user to not affect tbl_login_time at all. Normally users get marked as inactive, but every once in awhile a user gets deleted entirely yet the logs need to be maintained.
The MySQL docs lists 6 options for ON DELETE, and none of them sound appropriate:
RESTRICT: Would prevent the deletion in tbl_user.
NO ACTION: Gets evaluated just like RESTRICT.
CASCADE: Would delete in tbl_user like I want, but also in tbl_login_time.
SET NULL: Would delete in tbl_user, and leave the row in tbl_login_time but nulls out the data. Close but no cigar.
SET DEFAULT: MySQL recognizes it, but rejects it.
Omit ON DELETE: Equivalent to RESTRICT.
I've never used a foreign key like this before (enforce INSERT and UPDATE but not DELETE), and after reading a lot of other questions it doesn't seem like anyone else does either. That should probably tell me this is the wrong approach, but can it work somehow?
My problem is that I want the foreign key enforced for inserts,
updates to be cascaded, but deleting records in tbl_user to not affect
tbl_login_time at all.
You can't accomplish that with a foreign key constraint.
In some applications, ON DELETE SET NULL makes sense. But your application is essentially a log file stored in a SQL database. You have a significant problem in that you want to delete identifying information (users), but retain their ID numbers in some cases. I frankly don't understand why you're willing to retain the fact that user 23332 logged in at 18:40 today, while not caring whether you can identify who user 23332 is.
You have a few options.
Drop the logfile table, and store logfile data in a file in the filesystem, not in the database. If I were in your shoes, I'd consider this first. If we're talking about a database that's somehow accessible over the web, make sure the log file is stored outside the web root. (I'd store it under /var/log with all the other log files.)
Use foreign key constraints, and never delete a user.
Use foreign key constraints, and live with the effect of ON DELETE SET NULL or ON DELETE SET DEFAULT. In this particular application ON DELETE SET NULL and ON DELETE SET DEFAULT are semantically equivalent. Both replace good data with data that doesn't identify the user. If you can't identify user 23332 anyway, who cares whether you know she logged in at 18:40 today?
Drop the foreign key constraints, and use triggers to do whatever you like.
I'm pretty sure we agree that the most obvious option--use foreign keys with ON DELETE CASCADE--is simply wrong for your application.

Adding constraints in phpMyAdmin

I feel like I'm being stupid, but I can't find anywhere on the phpMyAdmin interface to add constraints to foreign keys e.g. CASCADE ON DELETE
I've looked for similar questions on here and on the phpMyAdmin wiki but I can't find anything about it.
I realise I could do this via the query interface, but I'd like to know how to do it through the graphical interface.
First, you should have your storage engine as InnoDB. Then select a table and go to 'Structure' tab.
Under the table you will see 'Relation view', click it. From there you could add constraints.
CASCADE
Whenever rows in the master (referenced) table are deleted (resp. updated), the respective rows of the child (referencing) table with a matching foreign key column will get deleted (resp. updated) as well. This is called a cascade delete (resp. update[2]).
RESTRICT
A value cannot be updated or deleted when a row exists in a foreign key table that references the value in the referenced table. Similarly, a row cannot be deleted as long as there is a reference to it from a foreign key table.
NO ACTION
NO ACTION and RESTRICT are very much alike. The main difference between NO ACTION and RESTRICT is that with NO ACTION the referential integrity check is done after trying to alter the table. RESTRICT does the check before trying to execute the UPDATE or DELETE statement. Both referential actions act the same if the referential integrity check fails: the UPDATE or DELETE statement will result in an error.
SET NULL
The foreign key values in the referencing row are set to NULL when the referenced row is updated or deleted. This is only possible if the respective columns in the referencing table are nullable. Due to the semantics of NULL, a referencing row with NULLs in the foreign key columns does not require a referenced row.
Firstly, you should choose storage engine as InnoDB.
Follow this way: click database_name -> More -> Designer

delete 1 record from a table and automatically delete all records associated with the user_id in mysql

I have been trying to learn from reading tutorials online and stuff but I just can't put my finger on it.
I have 2 tables at the moment (i'll have a lot more later on as I build my application) so I want to knock out this issue before expanding and coding.
I have my tables set to use InnoDB and I have each table related to each other by using user_id as foreign keys.
If i issue a DELETE query on the main users table, how can i get all records from other tables that are linked to the user_id field get deleted as well?
I know its simple, but I think I just need to ask the question myself so I can understand the answer rather than reading the answer... heh
thank a lot for any help.
Since they are InnoDB tables with proper FK relationships, you can simply use ON DELETE CASCADE in the foreign key definition. For example in one of the related tables:
FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE
However, this requires altering your existing schema to modify the foreign keys.
See the MySQL FOREIGN KEY docs for complete information.
You can cascade the DELETE using a DELETE trigger or using the technique Michael suggested, or you can manually write enough DELETE statements (deleting the entities from the bottom of the hierarchy upwards so you don't violate the foreign key constraints) though the latter is clearly not an ideal solution in terms of maintenance.
You may wish to use the TRIGGER approach if you want to fine tune the delete process (e.g. in case you don't want to destroy certain data related to the foreign key, or if you wanted to move it elsewhere or associate it to a different ID).
this is mysql example
ALTER TABLE table_with_foregin_key ADD FOREIGN KEY ( foreign_key_column )
REFERENCES table_name (
user_id
) ON DELETE CASCADE ON UPDATE CASCADE;

Foreign key constraints: When to use ON UPDATE and ON DELETE

I'm designing my database schema using MySQL Workbench, which is pretty cool because you can do diagrams and it converts them :P
Anyways, I've decided to use InnoDB because of it's Foreign Key support. One thing I noticed though is that it allows you to set On Update and on Delete options for foreign keys. Can someone explain where "Restrict", "Cascade" and set null could be used in a simple example?
For example, say I have a user table which includes a userID. And say I have a message table message which is a many-to-many which has 2 foreign keys (which reference the same primary key, userID in the user table). Is setting the On Update and On Delete options any useful in this case? If so, which one do I choose? If this isn't a good example, could you please come up with a good example to illustrate how these could be useful?
Thanks
Do not hesitate to put constraints on the database. You'll be sure to have a consistent database, and that's one of the good reasons to use a database. Especially if you have several applications requesting it (or just one application but with a direct mode and a batch mode using different sources).
With MySQL you do not have advanced constraints like you would have in postgreSQL but at least the foreign key constraints are quite advanced.
We'll take an example, a company table with a user table containing people from theses company
CREATE TABLE COMPANY (
company_id INT NOT NULL,
company_name VARCHAR(50),
PRIMARY KEY (company_id)
) ENGINE=INNODB;
CREATE TABLE USER (
user_id INT,
user_name VARCHAR(50),
company_id INT,
INDEX company_id_idx (company_id),
FOREIGN KEY (company_id) REFERENCES COMPANY (company_id) ON...
) ENGINE=INNODB;
Let's look at the ON UPDATE clause:
ON UPDATE RESTRICT : the default : if you try to update a company_id in table COMPANY the engine will reject the operation if one USER at least links on this company.
ON UPDATE NO ACTION : same as RESTRICT.
ON UPDATE CASCADE : the best one usually : if you update a company_id in a row of table COMPANY the engine will update it accordingly on all USER rows referencing this COMPANY (but no triggers activated on USER table, warning). The engine will track the changes for you, it's good.
ON UPDATE SET NULL : if you update a company_id in a row of table COMPANY the engine will set related USERs company_id to NULL (should be available in USER company_id field). I cannot see any interesting thing to do with that on an update, but I may be wrong.
And now on the ON DELETE side:
ON DELETE RESTRICT : the default : if you try to delete a company_id Id in table COMPANY the engine will reject the operation if one USER at least links on this company, can save your life.
ON DELETE NO ACTION : same as RESTRICT
ON DELETE CASCADE : dangerous : if you delete a company row in table COMPANY the engine will delete as well the related USERs. This is dangerous but can be used to make automatic cleanups on secondary tables (so it can be something you want, but quite certainly not for a COMPANY<->USER example)
ON DELETE SET NULL : handful : if you delete a COMPANY row the related USERs will automatically have the relationship to NULL. If Null is your value for users with no company this can be a good behavior, for example maybe you need to keep the users in your application, as authors of some content, but removing the company is not a problem for you.
usually my default is: ON DELETE RESTRICT ON UPDATE CASCADE. with some ON DELETE CASCADE for track tables (logs--not all logs--, things like that) and ON DELETE SET NULL when the master table is a 'simple attribute' for the table containing the foreign key, like a JOB table for the USER table.
Edit
It's been a long time since I wrote that. Now I think I should add one important warning. MySQL has one big documented limitation with cascades. Cascades are not firing triggers. So if you were over confident enough in that engine to use triggers you should avoid cascades constraints.
http://dev.mysql.com/doc/refman/5.6/en/triggers.html
MySQL triggers activate only for changes made to tables by SQL statements. They do not activate for changes in views, nor by changes to tables made by APIs that do not transmit SQL statements to the MySQL Server
http://dev.mysql.com/doc/refman/5.6/en/stored-program-restrictions.html#stored-routines-trigger-restrictions
==> See below the last edit, things are moving on this domain
Triggers are not activated by foreign key actions.
And I do not think this will get fixed one day. Foreign key constraints are managed by the InnoDb storage and Triggers are managed by the MySQL SQL engine. Both are separated. Innodb is the only storage with constraint management, maybe they'll add triggers directly in the storage engine one day, maybe not.
But I have my own opinion on which element you should choose between the poor trigger implementation and the very useful foreign keys constraints support. And once you'll get used to database consistency you'll love PostgreSQL.
12/2017-Updating this Edit about MySQL:
as stated by #IstiaqueAhmed in the comments, the situation has changed on this subject. So follow the link and check the real up-to-date situation (which may change again in the future).
You'll need to consider this in context of the application. In general, you should design an application, not a database (the database simply being part of the application).
Consider how your application should respond to various cases.
The default action is to restrict (i.e. not permit) the operation, which is normally what you want as it prevents stupid programming errors. However, on DELETE CASCADE can also be useful. It really depends on your application and how you intend to delete particular objects.
Personally, I'd use InnoDB because it doesn't trash your data (c.f. MyISAM, which does), rather than because it has FK constraints.
Addition to #MarkR answer - one thing to note would be that many PHP frameworks with ORMs would not recognize or use advanced DB setup (foreign keys, cascading delete, unique constraints), and this may result in unexpected behaviour.
For example if you delete a record using ORM, and your DELETE CASCADE will delete records in related tables, ORM's attempt to delete these related records (often automatic) will result in error.