Short
My tables structure looks like that
And here is Sql statements
$stmt = $this->db->prepare("DELETE FROM chapters WHERE (subject_id=? AND id = ?)") or die($this->db->error());
$stmt->bind_param("ii", $subject_id, $node);
$stmt->execute();
$stmt = $this->db->prepare("DELETE FROM sections WHERE (subject_id=? AND chapter_id=? )") or die($this->db->error());
$stmt->bind_param("ii", $subject_id, $node);
$stmt->execute();
$stmt = $this->db->prepare("DELETE FROM paragraphs WHERE (subject_id=? AND chapter_id=?)") or die($this->db->error());
$stmt->bind_param("ii", $subject_id, $node);
$stmt->execute();
So what I want to do is, to merge this 3 statements into one and optimize server load.
Detailed
For ex., if I want to delete row with id=1 from chapters table, then also delete from 2 more tables: sections, paragraphsby 2 parameters: $node and $subject_id (Of course, If there is rows with those parameters. I mean there must be join to prevent any error).
Question is..
Is that possible? I can't figure out, how sql statement must look like. Any suggestions?
If you have set up foreign key constraints with ON DELETE CASCADE then you only need to delete the parent row. MySQL will then delete the child rows automatically.
How do I use on delete cascade in mysql?
I haven't tried it, but you could try multi-table deletes:
http://dev.mysql.com/doc/refman/5.6/en/delete.html#id933512
DELETE chapters, sections, paragraphs
FROM chapters
LEFT JOIN sections ON sections.subject_id = $subject_id AND sections.chapter_id = $node
LEFT JOIN paragraphs ON paragraphs.subject_id = $subject_id AND paragraphs.chapter_id = $node
WHERE chapters.subject_id = $subject_id AND chapters.id = $node
I'm not sure if using left joins is really faster than using 3 separate deletes.
Related
Is there any possibility to duplicate specific entries by auto updating context sensitive relations?
Given a table 'table1' like:
My goal is to duplicate all entries with categoryId 42 while updating parentId if neccessary:
id is an auto incremented column and parentId is used to identify relations between the entries.
Currently I'm inserting them one by one, selecting the old data and managing the logic for the parentId in PHP.
//Thats simplified what I do ($conn is a Zend_Db_Adapter_Abstract)
$rows = $conn->fetchAll("SELECT * FROM table1 WHERE categoryId = 42 ORDER BY parentId ASC");
$newIds = [];
foreach($rows as $row){
if(array_key_exists($row['parentId'],$newIds))
$row['parentId'] = $newIds[$row['parentId']];
else
$row['parentId'] = null;
$conn->query("INSERT INTO table1 (parentId,info,categoryId) VALUES (?,?,?)",[$row['parentId'],$row['info'],$row['categoryId']]);
$newId = $conn->lastInsertId('table1');
$newIds[$row['id']] = $newId;
}
I'm stuck with this because I need the lastInsertedId of the new element to set the new parentId for the next one.
But I'm experiencing this to be pretty slow (in relation to one single query which contains the logic).
Is there any possibility to give a query some kind of incremental element sensitive logic? Or have you any suggestions on how to fasten this up?
Any help appreciated! :)
You are not showing any code. I guess you use mysqli_insert_id() to get the last inserted ID. Maybe you can do something with MAX(ID)+1.
Something like:
INSERT INTO table1
SELECT MAX(ID)+1,
info,
categoryId
FROM
table1
WHERE .............. -- the conditions to retreive the parent
How can I get a row's info from another table without having to SELECT each loop?
//get posts from "posts" table
$stmt = $dbh->prepare("SELECT * FROM posts WHERE user_id = :user_id");
$stmt->bindParam(':user_id', $userId);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($result as $row) {
echo $row['post'];
//get poster's full name from "users" table
$stmt = $dbh->prepare("SELECT * FROM users WHERE user_id = :user_id");
$stmt->bindParam(':user_id', $row['poster_id']);
$stmt->execute();
$result2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($result2 as $row2) {
echo $row2['full_name'];
}
}
how can I make this code more efficient and faster?
imagine if i have 1000 posts and each is posted by a different user. i need to get the full name of that user that posted the post. right now, i need to SELECT 1000 times because of those 1000 users. it seems so inefficient right now. how can i make it better?
I heard join might work? what are the solutions?
SELECT * FROM posts
JOIN users ON posts.user_id = users.id
WHERE posts.user_id = :user_id.
You are right, joining the users table onto your posts query will be faster.
Something else you can do to increase performance is to cache the results of your query in something like memcache and then clear the cache when a post is added or deleted. That way you don't need to hit your db every time this data is needed.
The problem I am trying to solve in a better way is to delete a folder with images that have tags. So for each image I need to delete
-the image itself
-tags of that image from three databases (img_offer, img_member, img_horses)
At the moment I get all image ids of the folder to be deleted and then iterate over these four times with the four different queries, which seems pretty inefficient.
The main problem is that as far as I know you can't have multiple prepare statements open at the same time and creating the statements new in each iteration seems also counter-intuitive.
What I think would be the best approach would be something like a multiple query prepare statement but I couldn't find anything similar so maybe someone here has an idea how to solve this in a cleaner way
My idea would be something like
$multiplePreparedStatement= "DELETE this FROM that WHERE id=?;
DELETE this2 FROM that2 WHERE id2=?;
DELETE this3 FROM that3 WHERE id3=?;";
$preparedStmt = $conn->prepare($multiplePreparedStatement);
foreach($imgArray as $imgId){
$preparedStmt->bind_param("iii", $imgId, $imgId, $imgId);
$preparedStmt->execute();
}
$preparedStmt->close();
But I don't think that would work as multiple SQL Queries are not supported in prepared statements or are they?
Here is my current code:
$id=$_GET['deleteAlbum'];
$getImages = "SELECT image_id AS id
FROM Images
WHERE folder_id = ?";
$deleteImage="DELETE FROM Images
WHERE image_id=?";
$deleteOffer = "DELETE FROM Images_Offers
WHERE image_id=?";
$deleteHorse = "DELETE FROM Images_Horses
WHERE image_id=?";
$deleteTeam = "DELETE FROM Images_Team
WHERE image_id=?";
//get all image ids
$ImgStmt=$conn->prepare($getImages);
$ImgStmt->bind_param("i", $id);
$ImgStmt->execute();
$ImgStmt->bind_result($id);
$imgToDelete = array();
while($ImgStmt->fetch()){
array_push($imgToDelete, $id);
}
$ImgStmt->close();
$stmt=$conn->prepare($deleteOffer);
foreach ($imgToDelete as $imgId){
$stmt->bind_param("i",$imgId);
$stmt->execute();
}
$stmt->close();
$stmt=$conn->prepare($deleteHorse);
foreach ($imgToDelete as $imgId){
$stmt->bind_param("i",$imgId);
$stmt->execute();
}
$stmt->close();
$stmt=$conn->prepare($deleteTeam);
foreach ($imgToDelete as $imgId){
$stmt->bind_param("i",$imgId);
$stmt->execute();
}
$stmt->close();
$stmt=$conn->prepare($deleteImage);
foreach($imgToDelete as $imgId){
unlink("../assets/img/images/img".$imgId.".jpg");
$stmt->bind_param("i",$imgId);
$stmt->execute();
}
$stmt->close();
I also had the idea of creating multiple connections but I think that might get problematic if e.g. delete an image while I still have a query iterating over images.
You do not have to iterate over image_id (at least not for the SQL data) at all. You can delete from the database everything associated with a particular folder_id in one go:
DELETE Images, Images_Offers, Images_Horses, Images_Team
FROM Images
LEFT JOIN Images_Offers ON Images_Offers.image_id = Images.image_id
LEFT JOIN Images_Horses ON Images_Horses.image_id = Images.image_id
LEFT JOIN Images_Team ON Images_Team.image_id = Images.image_id
WHERE folder_id = ?;
Of cause, before that you should unlink the actual files.
I have been trying this for hours and still no luck. Simply I am deleting rows from two tables using checkboxes. Let's assume I have checked two results and hit delete, then those two rows should be deleted from both tables.
In the below code the first query deletes two rows but the second one only deletes one row. If I just run each query separately then they both delete two rows? I have tried many times but I am not able to achieve what I wanted.
Can someone pleaseeee tell me why each query is failing to delete two rows? or is there a better way to do this? some kind of alternative?
$stmt1 = $mydb->prepare("DELETE from laptop where username = ? and id = ?");
echo $mydb->error;
foreach ($_POST['id'] as $id)
{
$stmt1->bind_param('ss', $username->username, $pdata);
$stmt1->execute();
}
$stmt2 = $mydb->prepare("DELETE from search where username = ? and id = ?");
echo $mydb->error;
foreach ($_POST['id'] as $id)
{
$stmt2->bind_param('ss', $username->username, $id);
$stmt2->execute();
}
Why not combine into one query?
$stmt1 = $mydb->prepare("DELETE from laptop, search where username = ? and id = ?");
In your example above the first query is looping over $_POST['id'], but it's always using the same value $pdata as the param for id=?.
If that query is deleting multiple rows, then you've got rows with duplicate username/id in that table. You should remove the loop, as you are doing extra work running the same delete query multiple times.
You mentioned that the first query was working and the second one wasn't though.
If that's the case, could you echo out the value of $id within your loop and verify that it is looping twice and the values are what you're expecting?
I am working on a cron script (in PHP PDS for a MySQL db) that will delete data older than 30 days from the database. Ive tried to do joining and on delete cascaded on my tables and statements but they haven't worked out so I went with a simpler approach here to get it working however I know this isn't the most processor effective method.
Please note that some of these tables (especially the links table) have hundreds of thousands of rows so that's why I wanted to be a little more clever than what I have here.
The table structure is easy, there is a table key table that has a time stamp and an id. That id is repeated in all tables as tablekey_id.
My current cron job is as follows.
/* Cron script to delete old data from the database. */
if (#include dirname(dirname(dirname(__FILE__))) . '/dbconn/website_connections.php') {
$conn = $connectionFromOtherFile;
$keyTable = 'table_key';
$linksTable = 'links';
$domainsTable = 'link_domains';
$terms = 'searched_domains';
$query = $conn->query("SELECT `id` FROM `$keyTable` WHERE `timestamp` < (NOW() - INTERVAL 30 DAY)");
$query->setFetchMode(PDO::FETCH_ASSOC);
while($row = $query->fetch()) {
$conn->exec("DELETE FROM `$linksTable` WHERE `tablekey_id` = $row[id]");
$conn->exec("DELETE FROM `$domainsTable` WHERE `tablekey_id` = $row[id]");
$conn->exec("DELETE FROM `$terms` WHERE `tablekey_id` = $row[id]");
$conn->exec("DELETE FROM `$keyTable` WHERE `id` = $row[id]");
}
}
Is there any way to get this into one statement? Please and thank you for the assistance.
EDIT: Heres what I ended up with.
/* Cron script to delete old data from the database. */
if (#include dirname(dirname(dirname(__FILE__))) . '/dbconn/website_connections.php') {
$conn = $connectionFromOtherFile;
$keyTable = 'table_key';
$linksTable = 'links';
$domainsTable = 'link_domains';
$terms = 'searched_domains';
$delete = $conn->prepare("DELETE tablekey, linktable, domaintable, searched
FROM `$keyTable` AS tablekey
LEFT OUTER JOIN `$linksTable` AS linktable on tablekey.id = linktable.tablekey_id
LEFT OUTER JOIN `$domainsTable` AS domaintable on tablekey.id = domaintable.tablekey_id
LEFT OUTER JOIN `$terms` AS searched on tablekey.id = searched.tablekey_id
WHERE tablekey.id = :tablekey_id");
$query = $conn->query("SELECT `id` FROM `$keyTable` WHERE `timestamp` < (NOW() - INTERVAL 30 DAY)");
$query->setFetchMode(PDO::FETCH_ASSOC);
while($row = $query->fetch()) {
$delete->execute(array('tablekey_id' => $row['id']));
}
}
You should be able to delete with one query like this:
DELETE kt, lt, dt, t
FROM `$keyTable` AS kt
LEFT OUTER JOIN `$linksTable` AS lt on kt.id = lt.tablekey_id
LEFT OUTER JOIN `$domainsTable` AS dt on kt.id = dt.tablekey_id
LEFT OUTER JOIN `$terms` AS t on kt.id = t.tablekey_id
WHERE kt.id = $row[id]
Note that I used outer joins here as I wasn't sure if the tables other than keyTable would be guaranteed to have records with that tablekey_id. If they are, you could use an inner join.
If tablekey_id is indexed, this should be fine even with thousands of rows. If you want to keep your database schema simple, I would keep 4 queries, deleting is not that CPU intensive.
If you really want one statement, you will have to complicate things and use a cascading deletion with foreign keys constraints:
CASCADE: Delete or update the row from the parent table, and automatically delete or update >the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. Between two tables, do not define several ON UPDATE CASCADE clauses that act on the same column in the parent table or in the child table.
Source: http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html
And this is admitting that your database is innoDB
first you want to retrieve the list of IDs like you are doing now, then you want to construct 4 queries like this:
DELETE FROM 'table' where 'id' IN ('id1', 'id2', 'id4')
this should prove a LOT more efficient then doing separate deletes. It would reduce the amount of queries to 5 in stead of 1+ 4N
As far as I know PDO only accepts one statement...
You could look at a trigger on deleting from the first (or you're main) table...
Sometimes I use this to solve my problems. Put the ids in string first.
$ids = array();
while($row = $query->fetch()) {
$ids[] = $row[id];
}
$ids_str = implode(',', $ids);
$conn->exec("DELETE FROM `$linksTable` WHERE `tablekey_id` in ($ids_str) ");
$conn->exec("DELETE FROM `$domainsTable` WHERE `tablekey_id` in ($ids_str)");
$conn->exec("DELETE FROM `$terms` WHERE `tablekey_id` in ($ids_str)");
$conn->exec("DELETE FROM `$keyTable` WHERE `id` in ($ids_str)");