I have a many-to-many relation, implemented with an association table in MySQL. I have a table for children and a table for parents. A child can have multiple parents, saved in the parent_child_link association table with their ID's.
Children can be updated through a HTML form, the parents are in a HTML multi-select. Now I need to update the record in the database, but my solution is not very efficient. Here's in pseudocode what I do:
Update the child information where child_id=x
Delete all the current associations in parent_child_link where child_id=x
Insert the new associations
This solution works great, but when the the parents weren't changed, e.g. only the name of the child was changed, then there are 2 unnecessary queries executed. How can I avoid those unnecessary queries? Is there some way to check if the parents in the multi-select didn't change?
Ofcourse I could just ignore all this hassle, because it already works, but I really like to keep things as efficient as possible.
I have the same question and figured out my solution as I was reading.
When I am ready to process the submitted entries, I first do a query to get the current associations and call that array $original_list. The submitted list I will call $submitted_list.
$original_list = array(3,5,7);
$submitted_list = array(1,2,3);
Then I just need to figure out 1) which items to delete (no longer exist) and 2) which items to add (new associations). Items in both lists do not get touched.
$delete_list = array_diff($original_list, $submitted_list);
$insert_list = array_diff($submitted_list, $original_list);
foreach($delete_list as $item) {
// delete $item from DB
}
foreach($insert_list as $item) {
// insert item in db
}
Would love to know if others feel this a valid solution.
Try solving it in the database, not in the application layer by using ON UPDATE CASCADE and ON DELETE CASCADE in the definition of the child table.
A slightly revised example form the MySQL site:
CREATE TABLE parent (id INT NOT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE child (id INT, parent_id INT,
INDEX par_ind (parent_id),
FOREIGN KEY (parent_id) REFERENCES parent(id)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB;
Check out the docs here: http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html
EDIT: For your many-to-many relation you can use something like:
CREATE TABLE parent_child_link (
parent_id INT NOT NULL,
child_id INT NOT NULL,
PRIMARY KEY(parent_id, child_id),
FOREIGN KEY (parent_id) REFERENCES parent(id)
ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (child_id) REFERENCES child(id)
ON DELETE CASCADE ON UPDATE CASCADE
);
Hope this helps.
Your solution is fine.
In your case you could "optimize" the process by making a query to retrieve the parents and check with the multi-select data if any changes has occurred.
Then you only perform the two delete and insert queries if needed. The counterpart is that when you actually changed the parents, then there will be 3 queries instead of 2.
So you should ask you if the you are about to modify the parents very often. In this case you should stick to your original solution to avoid an extra select query.
If you think the parents won't be updated very often, then you can go with the above solution. When you update the child info only, only one query is performed. When you also update the parents, 3 queries are performed.
When you go with the second solution, the delete and insert queries can be optimized too to only perform what is required (only delete the parents that are not his parents anymore and only insert new parent links).
PHP array functions can be helpful for that.
If you want to keep you'r current way of doing it, but just optimizing, you could wrap the queries in IF statements.
Like:
if ( isset ( $parent_name_change )){
// run query
}
Related
I have comment system with table like this .
Table Design Image
If a person delete the comment then all the row associated with that comment_parent also get deleted
i can write php recursive function which select the row and delete it .
The problem is if the comment has more than 100 of comment_parent, then more than 100 query will run to delete that.
Is it possible to delete comment along with its comment_parentid only with sql query , it will be great if anyone can help , i search and found CTE (but it doesn't support in mysqli i think).
If you setup a foreign key with ON DELETE CASCADE for the comment_parentid column, deleting the parent item will automatically delete the child items (and their children, and their children, etc.).
At past, I was used to make a table relationship programmatically, which is quite handy since you don't need to make FK constraint to each table which have relation.
But, I wonder what is the differences or the advantages of giving a FK constraint to tables that have relation, instead of just creating an attribute and retrieve them programmatically (calls the tables where field = another table PK).
Just some information, I work on php independent MVC framework without any dependency to eloquent or something else.
Hope someone give me some short lesson on this :D Thank you and have a nice day!
There are certain principles that you should follow while coding and development, I can say that there is no issue whether or not you create a foreign key constraint to a table that has relation or not but you know that won't restrict the column to have only those values that are being referenced by it. So basically it is not a good DB Schema and may lead to inconsistencies. For example deleting a parent table's row you will have to manually delete the child table's row on the other hand if you have a foreign key constraint that to onDelete = cascade, your database will automatically take care of everything and there won't any inconsistencies.
Can I replace a table in DB through phpmyadmin. Table has two foreign keys and I can truncate it by disabling foreign key checks (or delete it completely), however when I try to reupload it, if I don't enable foreign key checks it doesn't connect to the tables it should (I can't click on foreign ID, usually it takes me to the related table). If I do enable foreign key checks I get an error
#1452 - Cannot add or update a child row: a foreign key constraint fails
Is there a way to somehow replace a table with the same table from the past by keeping the relations alive?
i think the table contains data ... try clearing the table before dropping it.
Actually "you don't need to disable foreign key checks" if you want to delete from (or truncate) only the table(s) where child records are stored. You should be able to delete all and re-insert them later. Please try it this way.
You need to disable the foreign key checks if you want to truncate the tables with primary keys without deleting the child records. (This is not reasonable though.)
The following steps in order should work fine without disabling any constraint. If you don't follow the order, it will fail. You can skip the steps 2 and 3 and use 1 and 4 only.
Delete all child records with foreign key pointing to a primary key.
Delete all master records with primary key.
Re-insert all master records with primary key.
Re-insert all child records with foreign key pointing to a primary key.
Anyway, the error you get sounds like when you try to insert the old data, the matching record is missing in the master table where the foreign key points to.
You can check:
Whether the data in the master tables (the tables with primary key where foreign keys point to) are also deleted or truncated. If so, you need to bring them back before the child records.
Whether the data in the master tables are not the same anymore and the consistency is lost with the old data. (If you are working on a production database, the data might be changing). If so, you need you need to bring the missing records back to the master table.
In mysql, I've got the following:
TABLE docs (
ID
title
content
)
TABLE specialDocs (
docID -> docs(ID)
userID -> users(ID)
)
So specialDocs is a subset of documents that can belong to users.
I am wondering: Is it possible to specify a cascade rule so that when a user is deleted, the docs that belong to that user are automatically deleted? (I realize it would be simple to do this if the pointer to users(ID) was a column within the table "docs". However I am unclear if it is possible with a join table like the above...)
Thanks in advance for your help
I wouldn't do that - your specialdocs table is a many-to-many table, so multiple users could be related to the same document. What you want, shouldn't be allowed without deleting the other references first - which a cascade delete won't do. And if you don't have referential integrity (IE: MyISAM table), then you'll have orphaned records in specialdocs to a document that no longer exists.
Cascade on delete is a referential integrity feature, so it won't be available for MyISAM tables - you'd have to use a trigger for that. Given the need to check for referential dependencies, I'd use a trigger for Innodb tables as well to get rid of the related specialdocs records first...
Lets say I have two tables, 'a' and 'b'. The relationship of 'a' to 'b' is one to many. Lets both tables have the field status. This field has the values 'active' and 'inactive'. If I set the field status to 'inactive', does MySQL have a way to cascade this down to all tables tied to the row I changed the status value for? Or would this have to be done at the program level?
Let me know if this isn't clear.
You could use a trigger on the parent table that updates all children as necessary. Otherwise you'll have to handle it at the same level of your application where you update the parent row.
I've never tried this, and I'm not sure if I would recommend it, but if you add the status column to your foreign key (so you have two columns: the primary key of table 'a' and status) and then use on update cascade that might work as well.