elegant solution for multiple prepare statements - php

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.

Related

PHP Get info from another table

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.

PHP/MYSQL:Carry out UPDATE within SELECT query

There are many questions on SO about this but I cannot find one that quite meets my situation.
I want to use the values in some fields/columns of a table to set the value of a third field/column
In other words something like:
table races
athleteid|difficulty|score|adjustedscore
$sqlSelect = "SELECT athleteid,difficulty,score FROM races";
$res = mysql_query($sqlSelect) or die(mysql_error());
while ($row = mysql_fetch_array($res)){
$adjustedscore=difficulty*score;
$sqlupdate = "UPDATE race, set adjustedscore = '$adjustedscore' WHERE athletes = 'athletes'";
$resupdate = mysql_query($sqlupdate);
}
My understanding, however, is that MYSQL does not support update queries nested in select ones.
Note, I have simplified this slightly. I am actually calculating the score based on a lot of other variables as well--and may join some tables to get other inputs--but this is the basic principal.
Thanks for any suggestions
You can run:
UPDATE `races`
SET `adjustedscore` = `difficulty` * `score`
WHERE `athleteid` IN (1, 2, 3, ...)
First of all, as previous commentators said, you should use PDO instead of mysql_* queries.
Read about PDO here.
When you'll get data from DB with your SELECT query, you'll get array. I recommend you to use fetchAll() from PDO documentation.
So, your goal is to save this data in some variable. Like you did with $row.
After that you'll need to loop over each array and get your data:
foreach($row as $r) {
//We do this to access each of ours athlete data
$adjustedscore= $row[$r]["difficulty"]* $row[$r]["score"];
//Next row is not clear for me...
$query = "UPDATE race SET adjustedscore = '$adjustedscore' WHERE athletes = 'athletes'";
And to update we use PDO update prepared statement
$stmt = $dbh->prepare($query);
$stmt->execute();
}

prepared statements execute multiple queries at once

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?

Avoiding a query inside a loop in my situation

I have quite a complicated situation here. I can't find a better way to solve this without putting a SELECT query inside a loop that rolls over 70000 times when I enter that page (don't worry, I use array_chunk to split the array into pages). I guess this would be a resource killer if I use a query here. Because of this, here I am, asking a question.
I have this big array I need to loop on:
$images = scandir($imgit_root_path . '/' . IMAGES_PATH);
$indexhtm = array_search('index.htm', $images);
unset($images[0], $images[1], $images[$indexhtm]);
Now I have an array with all file names of the files (images) in my IMAGES_PATH. Now the problem comes here:
Some of these images are registered on the database, because registered users have their images listed on my database. Now I need to retrieve the user_id based on the image name that the array above gives me.
Inside a loop I simply did this:
foreach ($images as $image_name)
{
$query = $db->prepare('SELECT user_id FROM imgit_images WHERE image_name = :name');
$query->bindValue(':name', $image_name, PDO::PARAM_STR);
$query->execute();
$row = $query->fetch(PDO::FETCH_ASSOC);
$user_id = $row['user_id'];
echo $user_id;
}
This works just fine, but the efficiency equals to 0. Using that user_id I plan on getting other stuff from the imgit_users table, such as username, which would require another query inside that loop.
This is too much and I need a simpler way to deal with this.
Is there a way to get those user_ids before going inside the loop and use them IN the loop?
This is the table structure from imgit_images:
While this is the schema for imgit_users:
Something like this would work (I'm not sure if it's possible to prepare the WHERE IN query since the # of values is unknown... Else, make sure you sanatize $images):
$image_names = "'".implode("', '", $images)."'";
$query = $db->prepare("SELECT img.user_id, image_name, username
FROM imgit_images img
INNER JOIN imgit_users u ON u.user_id = img.user_id
WHERE image_name IN(".$image_names.")");
$query->execute();
while($row = $query->fetch(PDO::FETCH_ASSOC))
{
echo $row['user_id']."'s image is ".$row['image_name'];
}
You might need to tweak it a little (haven't tested it), but you seem to be able to, so I'm not worried!
Not sure if it is going to help, but I see a couple of optimizations that may be possible:
Prepare the query outside the loop, and rebound/execute/get result within the loop. If query preparation is expensive, you may be saving quite a bit of time.
You can pass an array as in Passing an array to a query using a WHERE clause and obtain the image and user id, that way you may be able to fragment your query into a smaller number of queries.
Can you not just use an INNER JOIN in your query, this way each iteration of the loop will return details of the corresponding user with it. Change your query to something like (i'm making assumptions as to the structure of your tables here):
SELECT imgit_users.user_id
,imgit_users.username
,imgit_users.other_column_and_so_on
FROM imgit_images
INNER JOIN imgit_users ON imgit_users.user_id = imgit_images.user_id
WHERE imgit_images.image_name = :name
This obviously doesn't avoid the need for a loop (you could probably use string concatenation to build up the IN part of your where clause, but you'd probably use a join here anyway) but it would return the user's information on each iteration and prevent the need for further iterations to get the user's info.
PDO makes writing your query securely a cinch.
$placeholders = implode(',', array_fill(0, count($images), '?'));
$sql = "SELECT u.username
FROM imgit_images i
INNER JOIN imgit_users u ON i.user_id = u.id
WHERE i.image_name IN ({$placeholders})";
$stmt = $db->prepare($sql);
$stmt->execute($images);
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// use $row['username']
}
Create a string of comma separated ?s and write them into IN's parentheses. Then pass the array of images to execute(). Easily done, and now all of your desired data is available in a single resultset from a single query. (Add additional columns to your query's SELECT clause as needed.)

Deleting rows from 3 tables at once in PHP

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.

Categories