Updating multiple table records in 1 query with PDO - php

Im trying to figure out how to update my table using PDO - I need to update 2 records simultaneously only im unsure how this works.
I have my session user, and my "befriended user".
My befriended user will have 10 credits or so in my table and he can offer other users X credits when they befriend him.
// $uid is my logged in session user
// $follow_id is the id of the person were trying to befriend
// Im confused by the whole process of this do i write 2 queries? 1 that minuses credits from the befirended user, and then one that adds the credits to the current session users column?
$upd_stmt = $conn->prepare('SELECT * FROM users WHERE user_id=? ');
$upd_stmt->bindParam(1, $uid, PDO::PARAM_INT);
$upd_stmt->execute();
while( $row = $upd_stmt->fetch(PDO::FETCH_ASSOC) ) {
$row['credits_offered'];
}

The short answer is, yes, you will need to write two queries, one to subtract and the other to add.
You could probably do it in one query, but I would recommend doing it in two for better readability. I would also recommend doing it via a transaction, if the first query executes successfully but the second doesn't, you would want to roll it back.
PHP + MySQL transactions examples

This is what transactions were made for. You don't need to do this in one query to make sure they are both executed succefully.
$dbh->beginTransaction();
$addQuery = "UPDATE ...";
$subtractQuery = "UPDATE ...";
$sth = $dbh->exec($addQuery);
$sth = $dbh->exec($subtractQuery);
$dbh->commit(); // or $dbh->rollback();
Read more about transactions here: http://en.wikipedia.org/wiki/Database_transaction
Short version - they ensure that either all your queries are executed succefully, or none.

Related

Can't fetch a single field from database [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
When I try to do it it fetches the entire column, not just one field.
$connection = mysqli_query($db, "SELECT * FROM users");
<?php
while($row = mysqli_fetch_array($connection)) {
?>
<p>Your balance is: <?php echo $row['balance']; ?></p>
<?php
}
?>
That was outputting
Your balance is: 5
Your balance is: 0
Your balance is:
So I tried
$query_for_selecting = mysqli_query($db, "SELECT balance FROM users");
<?php if (mysqli_num_rows($query_for_selecting) > 0) { ?>
<?php while($row = mysqli_fetch_assoc($query_for_selecting)) { ?>
<p>Your balance is <?php $row['balance']; ?></p>
<?php } ?>
<?php } ?>
And that wasn't outputting anything so eventually, I tried using a WHERE clause and a limit of 1
$query_for_selecting = mysqli_query($db, "SELECT * FROM users WHERE balance = '3' DESC LIMIT 1");
<?php if (mysqli_num_rows($query_for_selecting) > 0) { ?>
<?php while($row = mysqli_fetch_assoc($query_for_selecting)) { ?>
<p>Your balance is <?php $row['balance']; ?></p>
<?php } ?>
<?php } ?>
All I got was a white screen
I think a basic little tutorial might be in order here.
First off: SELECT * FROM users means: "give me everything in the users table". You will get the full table, every row and every column.
while($row = mysqli_fetch_array($connection)) will loop through every row your query returns. It will call mysqli_fetch_array() and put the result in $row until there are no more rows in your query's result.
If you only want to output a single row of data, you have three options:
Add a WHERE condition so that your query will only fetch a specific row
Add a LIMIT clause so that your query will only fetch a single row
Call mysqli_fetch_array() only once instead of in a while loop
From the comments in the discussion thread, it looks like you want to retrieve only the balance for the currently logged in user, and you have a session variable somewhere that tells you who that user is. That means you'll want to use a WHERE condition so that your query will only fetch the row for that specific user.
You haven't told us what that session variables is called or what the name is of the column in the users table that you can compare that session variable with, so I'll assume that your users table has an id column and your session variable is called user_id and should match the id value from your users table.
So let's say the user with id 123 is currently logged in. You'll want to end up with the query SELECT balance FROM users WHERE id = 123.
The quick solution is to change your code to:
$connection = mysqli_query($db, "SELECT balance FROM users WHERE id = " . $_SESSION['user_id']);.
This is bad code. We'll make it better, but try this first and see if it gets you the result you actually want. If it doesn't, let me know.
The reason this is bad code is because adding variables to a query string like this dramatically increases the risk of SQL injections. If there's any possibility at all that the value of the variable comes from user input, then at some point a user will figure that out and make sure it contains something that will break your application.
Best case scenario: the page simply won't render for that one user.
Bad case scenario: the user will be able to read out your entire database and will sell sensitive user data to the highest bidder.
Worst case scenario: the user will be able to inject some of their own Javascript code into your database in a column you're not sanitizing before rendering, letting them capture and intercept passwords and/or financial information your users are entering on your site and they will then use that information to make life miserable for all of your users.
So you don't want to just drop $_SESSION['user_id'] into your query like that. Instead, you'll want to use a prepared statement and let the database handle the problem of dropping the variable into the query.
First, you'll need to prepare your query:
$statement = mysqli_prepare($db, "SELECT balance FROM users WHERE id = ?");
The ? symbol is a placeholder where you can bind a parameter. Let's do that:
$statement->bind_param("i", $_SESSION['user_id']);
The "i" tells MySQL that you're binding an integer value. If you're not matching against a user id but a username, for example, you'll want to instead use "s" to tell MySQL you're binding a string value.
Now you can execute the query and get the result. Putting it all together:
$statement = mysqli_prepare($db, "SELECT balance FROM users WHERE id = ?");
$statement->bind_param("i", $_SESSION['user_id']);
$statement->execute();
$connection = $statement->get_result();
Let us know if that works. Some tweaking might be required.
I have a feeling as to what's going on. You're fetching the entire database without either using a LIMIT of 1 and/or use a WHERE clause, given that you have unique ID's somewhere for columns. I am sure that you have more than the one record in your database table.
I was going to post this in a comment but decided not to. Stack doesn't really want us to do that, (edit) and at this point, it is way too long for a comment.
#ADyson "I initially wanted to display the balance of the user that's logged in, but that didn't work out." – markthedark
About that. It seems that what you are looking for is to get the balance for a user/record in particular. For that, you definitely need to use a WHERE clause.
If your query failed, enable error reporting and checking for errors on the query.
References:
https://www.php.net/manual/en/function.error-reporting.php
https://www.php.net/manual/en/mysqli.error.php
Plus, the $i = 0; and $i++; may not be helping. It's hard to say what that is supposed to do. I know the syntax, I just don't know why you're wanting to increase it.
Side note: I would avoid in using $connection as a query variable assignment. It could be confusing. Try to use something clear like $query_for_selecting.

Oracle Multiple queries with locked row

I'm using ORACLE version 11g.
I would like to execute three queries "at the same time" and take care that if one or more of theses queries fails must return both tables to the previous state. These queries are one select to know if the selected row still being possible the make the action, and one update and one insert to do the action.
In my case I need to make an update on the same locked row (obviously no one else should be able to do the action to the same row) and later and insert on another table, only if the result of the select query confirm that the selected row still having the option to execute the action, so the queries will be like these approximately:
//this is the row I want to execute the action
$selectedIdFromTable1 = "1";
$query="SELECT attr1 FROM table1 WHERE attr1 = 'oldValueAttr1' AND id = selectedIdFromTable1";
$stmt = $this->oracleDB->prepare($query);
$stmt->bindValue(1, $attr1, "string");
$stmt->execute();
$result = $stmt->fetchColumn();
if($result->num_rows == 1){ //I'm still being able to do the action to the row because the row still having the oldValue
//So here the row must be locked to execute the update and the insert only once. Only one user should execute the update and the insert.
$query="UPDATE table1 SET attr1 = ? WHERE id == $selectedIdFromTable1";
$stmt = $this->oracleDB->prepare($query);
$stmt->bindValue(1, 'newValueAttr1', "string");
$stmt->execute();
$query="INSERT INTO table2 (attr2) VALUES (?)";
$stmt = $this->oracleDB->prepare($query);
$stmt->bindValue(1, 'newValueAttr2', "string");
$stmt->execute();
}
//here the lock can release the row for future actions (but not this one, because if any one tries the previous select should not find anymore the selected row)
Also I'm using the binding system to send the variables more safety. Not sure If can affect the answer.
I'm quite sure that a transaction with locking row is the answer and if it's the answer, I will really appreciate to receive your help with an example of a transaction with Oracle with an example of this situation.
All of that, will be in a Symfony 3.3 project. Probably is not necessary this last information, but the transaction code must be in the symfony project and not in the oracle database for different reasons.
Thank you very much.
If you will use symfony you will most likely use the DBAL connection.
Transactions are handled as described in its documentation
(To me it seems more a transaction feature than a locking one)
Transactions:
$conn->beginTransaction();
try{
// do stuff
$conn->commit();
} catch (\Exception $e) {
$conn->rollBack();
throw $e;
}
Locking is not handled by DBAL

php mysqli update with fetched

I'm trying to update a mysql database with data I fetched. (btw I need to do this for specific individual items, but that's not the problem.) When it comes to creating separate statements for fetching or updating I can do that. Separately, I'm able to fetch data like this:
$query = "SELECT starting_amount FROM comp ORDER BY item DESC LIMIT 3, 1";
$result = $conn->query($query);
$row = mysqli_fetch_assoc($result);
and I'm able to update data like this:
$sql = "UPDATE comp SET final_amount=25 WHERE item='Y'";
but I can't put the two together (I tried several ways and failed). In other words, I am able to update a table record with data that I manually type, e.g. I type "25" manually in the update statement, which in this example is the data from 'staring_amount', but I don't know how to update with a statement that will automatically use data I fetch from the table. Again in other words, how do I write the update statement so that "SET final_amount=" is followed by fetched data? Thanks in advance for any help!
So, you just need to pass your fetched data into the query
$starting_amount = $row['starting_amount'];
$sql = "UPDATE comp SET final_amount=$starting_amount WHERE item='Y'";
Firstly, I highly recommend looking into prepared statements - using a prepared statement to insert data is an easy way to prevent SQL injection attacks and also will make what you want to do a little easier.
Here's an example of a prepared update statement using mysqli based on your example:
$statement = $conn->prepare("UPDATE comp SET final_amount=? WHERE item='Y'")
$statement->bind_param(25);
I'll assume for this answer that you want to use just the first row of the resultset.
Using your example above, you can replace the value in bind_param with a value from your row.
$statement->bind_param($row['starting_amount']);
There's no need to do them as separate statements, since you can join queries in an UPDATE.
UPDATE comp AS c1
JOIN (SELECT starting_amount
FROM comp
ORDER BY item DESC
LIMIT 3, 1) AS c2
SET c1.final_amount = c2.starting_amount
WHERE c1.item = 'Y'

Which is the most secure and correct solution to display only one value from a single row using SELECT in PHP?

My question is which solution do you think is best to use when wanting to retrieve only one value from a MySQL DB row.
For example, let's say we have the following:
-table "users" with three rows "username","password" and "status" AND three users "user1","user2" and "user3".
If I want to select only the status of one user (let's say user1) and set it to a variable, I will use:
$user_status = mysql_result(mysql_query("SELECT status FROM users WHERE username='user1'"),0);
I searched the net and I see that people use different methods of retrieving this type of info, such as setting LIMIT 1 inside the select code or by retrieving the whole users list and then sort the one that matches their needs.
I am wondering if my solution is the best and secure way (including security from SQL inject, keeping in mind that no $_GET method is used in the php code).
Maybe use both LIMIT 1 and the method I used above (for the code to require less resources and time to execute)?
From a database point of view the safest way is to have a unique key in the table you are selection from and retrieve the row via this key. In your example you could have a userID column that holds a unique ID for each user. If you query WHERE userID='...' the database guarantees you that there can only be one result row.
Edit: "Public opinion" suggested that I add two things.
Thou shall not use mysql_*! Why not use mysqli? You should not have to worry about its performance.
There is no reason to use LIMIT 1 if you are using proper database design - no reason at all. Its a bit like writing code that says Enter car; Make sure you have entered exactly one car;. LIMIT can be used in other cases like retrieving the first 10 results of many.
you should use the PDO methods if you want any security, regular mysql calls have been fased out for a while
Use mysqli (http://ee1.php.net/mysqli), but check here http://www.php.net/manual/en/book.pdo.php
Your query though would be SELECT status FROM users WHERE username='user1' LIMIT 1
First one - you don't use mysql_ methods anymore - they're deprecated and natively unsafe.
Second - you use PDO interface, like this
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';
try {
$dbh = new PDO($dsn, $user, $password);
$stmt = $dbh->prepare("SELECT `status` FROM `users` WHERE `username` = ?");
$stmt->execute(array($_GET['username']));
$status = $stmt->fetchColumn();
} catch (PDOException $e) {
echo 'PDO Error : ' . $e->getMessage();
}
Using prepare - execute chain is safe with PDO - it is automatically sanitized, so you don't have to worry about mysql injections. At prepare part you create a query with parameter, and in execute part you execute your prepared query with parameter equaling $_GET and store the result. Any error you encounter along the way is being caught in the catch (PDOException $e) block and can be handled appropriately.

Detect a similar request sent to php from same client in a little time difference

I have a php script that receives $_['post'] data from a button in flash , some times the user does double click on that button and the data sends twice , it causes mysql update conflict in server side .
How can i ignore the further requests in php from the same client in a little time diffrence?
Have you ever heard of locking tables in mysql?
It's not possible to provide detailed example without you providing more details, but basic example would be:
mysql_query( "LOCK TABLES updates READ WRITE", $connection);
$q = mysql_query("SELECT COUNT(*) FROM updates WHERE user = $currentUser AND time...");
if( mysql_result($q, 0, 0) != 0){
// Someone's already doing update; exit
mysql_query( "UNLOCK TABLES;");
return false;
}
// Require lock for ourselves
mysql_query( "INSERT INTO updates ...");
mysql_query( "UNLOCK TABLES;");
// Do the stuff on database
This makes sure that when the same user will try to do this twice he or she won't be allowed to (just one update at the time).
You have also different options:
Use TRANSACTION
Generate one time token for updating (beware of atomicity, you'd have to execute DELETE FROM ... and then check affected rows, because SELECT; DELETE my get interrupted or you'd have to use table locking again)
And my favourite one: button.enable=false, and on request completed button.enabled=true
Note: the code is vulnerable against SQL Injection and you mysql_ functions are outdated.
Why not have a last updated timestamp in the database and then check that against the current time. If it's in the last couple of seconds, then don't run the MySQL query.
You can add basic protection mechanism, as you can add a hidden random value to the form and maintain a table of displayed values, from which you delete the used ones before executing the main query. This would also prevent XSS.

Categories