Mysqli recursive delete on same table - php

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.).

Related

mySQL: index table of 2 other table ID's

Ok, here's what I wanna do.
Table1(globalID [primary key, autoincremented] (is a global id i would use in PHP))
Table2 & table3(ID* [foreign primary key, referencing to globalID], other columns after this, different for each of the tables 2 and 3);
What I wannt to know is if there is a way, and how, to make it so that when I insert a row in table2 or table3 the globalID in talbe1 gets populated and it's value also inserted in table2 or table3 accordingly as an ID. I assume it would be accomplished with a trigger, but triggers are not my forte so if that's the only way it can be done please provide an example.
I was also thinking of expanding it to this scenario:
Table1(globalID [primary key, autoincremented] (is a global id i would use in PHP),
OtherID [an UUID]);
Table2 & table3(ID* [foreign primary key, referencing to OtherID], other columns after this, different for each of the tables 2 and 3);
But in that case there is still the almost nonexistent possibility that 2 users might generate the same UUID while inserting a new row in some table. I was wondering if i could avoid that by making the keys generated by the sql server automatically without me having to code it PHP server side.
If anyone has had this problem worked out and can also point out other things i need to watch out for or take into account please point them out. Also please provide an example of the solution.
EDIT:
#John B.
EDIT BY TosheX
thank you for the reply. I have been reading up on this in the meantime and i already have a solution outside of SQL (in php). Basically I use Yii PHP framework in which I create a model of the table, and create an active record to populate a new row. now when I do that the ID in table1 is generated, and afterwards I still have a variable that points to that populated row and I can just read the generated ID since it's automatically fetched (without having to check for the last record in the database, which may return the record someone created milliseconds after me). Afterwards I just need to create a row inside the appropriate table2 or 3 and assign the already generated value for the ID.
i found the idea here:
http://www.yiiframework.com/forum/index.php/topic/14593-alternate-for-mysql-insert-id/
I really wanted an inbuilt solution in the database, but since I have experience with databases and do know enough about triggers (enough to not like them :D ) i know it's a very tricky code to get right so I was wondering if there are alternatives to auto-populate or something.
Still, I really appreciate your response, since you took your time and all. I have been working in msSQL more too, and I find logic in what you said. UUID in mySQL is the same as GUID in msSQL and I have considered that too as you can see.
Taking all this into account I will go with the solution i found, but I will accept your answer since you did try to help out, and you did bring some good ideas to the table.
Thanks again.
I'm a mssql person, but I dont think it is possible, unless you can create a "BEFORE INSERT" trigger for your db (and even then it will be problematic), because:
Whatever the ID of Tables 2 & 3 is, be it an integer or a guid, these are primary keys: Being a primary key, this column will not allow nulls, yet what you require is essentially to create new rows in these tables, with this column null, and then generate a new row in Table1, and use the new value in that table to update the new row in either Table2 or Table3.
Therefore, you'll probably need to either:
Do a solution in code (ie outside of SQL) whereby you cause the creation of a new row in Table1 first, then use this value when subsequently creating in Table2/3, or:
On tables2/3 have a autoincremented column for your primary key lets call it "PK", then have ID as a separate ordinary FK but which allows nulls, plus a trigger as follows (though you might need to fiddle with the syntax a little bit, because I am a mssql person, and I don't know your db's syntax) :-(
Also this example is designed to work with a multiple row insert, and to achieve this uses a cursor, and my syntax for cursors is dodgy at best even in mssql!
CREATE TRIGGER trg_Table2 ON dbo.Table2
FOR INSERT /* might be AFTER INSERT etc */
AS BEGIN
SET nocount on;
Declare ICursor Scroll For SELECT PK FROM Inserted;
Declare #PK int,
#GlobalID int,
#ID guid;
OPEN ICursor
FETCH Next From ICursor INTO #PK
WHILE ##Fetch_Status BEGIN
INSERT Table1 (OtherID) VALUES (NewID());
SELECT #GlobalID = ##Identity
SELECT #ID = OtherID FROM Table1 WHERE GlobalID = #GlobalID
UPDATE Table2 SET ID = #ID WHERE PK = #PK
FETCH Next FROM ICursor INTO #PK
END
Close ICursor
Deallocate ICursor
END
Hope this helps

How do I keep track of just deleted rows in a table on MySQL?

I have foreign key constraints set on my MySQL tables that cascade on delete. I need to keep track of all the other tables and rows affected when I delete a user because there are physical files involved that must also be deleted. Is there a way to log this?
Well, by adding a trigger on each table that you'ld like to track you could easily:
feed an audit table
run a background job to delete the corresponding files
Like Mitch said, it also depends on you, what you're wanting to do exactly.
For instance you could also want to keep the rows for history purpose. In this case you might implement your own rows "suppression" by using a "deleted" flag in the tables, so you would put it to 1 whenever you want to remove a row. [...]
I think no.
But you can do a little trick. For example, before you delete a record in table A, select all record in other tables which is related to the deleted record.
$sql = "SELECT Id FROM b WHERE b.a_id ='$deleted_A_Id'";
//delete physical file and any other things
$sql = "DELETE FROM A where A.id = '$deleted_A_Id'";
//just exec sql the normal way

PHP update MYSQL many-to-many relation

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
}

Proper way of 'updating' rows in MySQL

This is my db structure:
ID NAME SOMEVAL API_ID
1 TEST 123456 A123
2 TEST2 223232 A123
3 TEST3 918922 A999
4 TEST4 118922 A999
I'm filling it using a function that calls an API and gets some data from an external service.
The first run, I want to insert all the data I get back from the API. After that, each time I run the function, I just want to update the current rows and add rows in case I got them from the API call and are not in the db.
So my initial thought regarding the update process is to go through each row I get from the API and SELECT to see if it already exists.
I'm just wondering if this is the most efficient way to do it, or maybe it's better to DELETE the relevant rows from the db and just re-inserting them all.
NOTE: each batch of rows I get from the API has an API_ID, so when I say delete the rows i mean something like DELETE FROM table WHERE API_ID = 'A999' for example.
If you retrieving all the rows from the service i recommend you the drop all indexes, truncate the table, then insert all the data and recreate indexes.
If you retrieving some data from the service i would drop all indexes, remove all relevant rows, insert all rows then recreate all indexes.
In such scenarios I'm usually going with:
start transaction
get row from external source
select local store to check if it's there
if it's there: update its values, remember local row id in list
if it's not there: insert it, remember local row id in list
at the end delete all rows that are not in remembered list of local row ids (NOT IN clause if the count of ids allows for this, or other ways if it's possible that there will be many deleted rows)
commit transaction
Why? Because usually I have local rows referenced by other tables, and deleting them all would break the references (not to mention deletete cascade).
I don't see any problem in performing SELECT, then deciding between an INSERT or UPDATE. However, MySQL has the ability to perform so-called "upserts", where it will insert a row if it does not exist, or update an existing row otherwise.
This SO answer shows how to do that.
I would recommend using INSERT...ON DUPLICATE KEY UPDATE.
If you use INSERT IGNORE, then the row won't actually be inserted if it results in a duplicate key on API_ID.
Add unique key index on API_ID column.
If you have all of the data returned from the API that you need to completely reconstruct the rows after you delete them, then go ahead and delete them, and insert afterwards.
Be sure, though, that you do this in a transaction, and that you are using an engine that supports transactions properly, such as InnoDB, so that other clients of the database don't see rows missing from the table just because they are going to be updated.
For efficiency, you should insert as many rows as you can in a single query. Much faster that way.
BEGIN;
DELETE FROM table WHERE API_ID = 'A987';
INSERT INTO table (NAME, SOMEVAL, API_ID) VALUES
('TEST5', 12345, 'A987'),
('TEST6', 23456, 'A987'),
('TEST7', 34567, 'A987'),
...
('TEST123', 123321, 'A987');
COMMIT;

How to automatically delete rows in a table when the corresponding rows in a 1 to 1 table are deleted?

I have two different kinds of document. One is organized by folder. The other is organized by client. Here is the table structure, in mysql:
Table docs
ID
title
Table folderDocs
docID -> docs(ID)
folderID -> folders(ID) ON DELETE CASCADE
Table clientDocs
docID -> docs(ID)
clientID -> clients(ID) ON DELETE CASCADE
I am looking for an elegant way to delete documents automatically when either a folder or a client is deleted. The above cascade rules don't quite accomplish this. (Ie the rows in folderDocs will be deleted, but the corresponding rows in docs will remain.)
Is there anyway to set this up in mysql, using cascade rules or some other method, so that when a row from folderDocs or clientDocs is deleted by cascade, the corresponding row in docs is also deleted? (I am hoping to avoid having to programatically delete the documents first, then delete the folder / client.)
Thanks (in advance) for your help.
Assuming that each document must belong to a folder or a client, then you may want to setup a scheduled cleanup job to delete all documents which do not have folders and clients using the query below
DELETE FROM docs USING docs
LEFT JOIN clientDocs c ON (c.docid = docs.id)
LEFT JOIN folderDocs f ON (f.docid = docs.id)
WHERE ISNULL(c.clientid) AND ISNULL(f.folderid)
I do not think you can do that in straight sql. you might need to write a stored procedure to do it.
But is it a good idea. cos the way your tables are designed makes it look like a many to many relationship. can you delete a doc if there are still references to it from a another folder?
If they are suppose to be a one to many relationship, then the foreign key to the folder and the client should be in doc and not a table by itself. then you can use cascade delete.
Edit: you could try using a trigger instead. http://dev.mysql.com/doc/refman/5.0/en/triggers.html
but it still requires that some codes be written (in the form of triggers)

Categories