I have a query like:
SELECT id, name, surname, fromId, toId, msg_text, readed FROM messages WHERE toId = 2;
So I want to update all selected rows.readed = 1. And Query must return all selected rows.
These action must do in one query if possibe.
Sorry for my english
Short answer: No, it is not possible in a single query.
A little less short answer: There is something known as a command-query separation which in short suggests that a command should do something and return nothing and a query should do nothing and return something. I recommend following this principle if you intend on building good software.
I wont get into why this is not possible because I myself am not that much of an SQL guru and I could only guess but I would suggest an easy solution to your problem.
When you get your results then you are most likely processing them in PHP. Assuming the results are sorted in ascending order - on the first iteration grab the minimum id and on the last one grab the maximum id, then run an update query:
UPDATE messages SET readed = 1 WHERE toId = ? AND (id >= <minimum id> AND id <= <maximum id>)
On a side note - name and surname are probably not what you want to store in a messages table.
You can update only with an UPDATE query. An UPDATE query can return only one thing: that is number of affected rows. So, you cannot update and select the value you need in a single query.
Have a look here Update a table then return updated rows in mySQL
You can do that with stored procedure.
Create a stored procedure which executes the update and the select as well.
Eg:
DROP PROCEDURE IF EXISTS myproc;
delimiter //
CREATE PROCEDURE myproc()
begin
set #qry='select id, name, surname, fromId, toId, msg_text, readed from messages where toId = 2';
prepare query from #qry;
execute query;
update messages set readed=1 where toId = 2;
end //
Then you can run the query through this procedure.
Eg:
$sql = "call myproc()";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
echo "id: " . $row["id"]. " - Name: " . $row["name"]. "<br>";
}
} else {
echo "0 results";
}
Related
there is table named Book and this code is supposed to find a specific record and edit it's num to num-1, but it only edit num to -1.
$sql = 'SELECT num FROM Book
WHERE bid="'.$_SESSION["sails bid{$i}"].'"';
if (mysqli_query($conn, $sql)) {
$row = mysqli_fetch_array($query);
$numbers=(int)$row['num']-1;
$sql='UPDATE Book
SET num="'.$numbers.'"
WHERE bid="'.$_SESSION["sails bid{$i}"].'"';
if (mysqli_query($conn, $sql)) {
...
}
}
For one thing, you've overcomplicating this. You don't need to select into PHP, perform the math, and then update. Simple arithmetic can be performed in SQL. For example:
UPDATE Book SET num = num - 1 WHERE bid = ?
Just bind your bid value (preferably using query parameters instead of the string concatenation you're currently using) and you only need to execute that one UPDATE instead of all the code you have now.
So I have the following MySQL query that I want to execute through a PHP script:
for($y=0;$y<$usercount;$y++){
$findweight = "replace into weight (user_id, column1)
select user_id,
0.4*column1_a+0.6*column1_bon as calc1
from history_summary where user_id=$y";
if (mysqli_query($link, $findweight)) {
echo "success <br>";
} else {
echo "Error: " . $sql . "<br>" . mysqli_error($link);
}
}
But somehow everytime I execute the script it fails to work. Everytime the script is executed, the text "success" is outputted, which means that the MySQL query should have been successfully executed, but when I check my database, nothing seems to have changed. Yet when I run the query directly such as the following:
replace into weight (user_id, column1)
select user_id,
0.4*column1_a+0.6*column1_bon as calc1
from history_summary where user_id=1
It works just fine. I'm assuming the issue is that the variable $y failed to be passed on properly to the query, but I'm not quite sure. I've tried the other solutions posted on this site but none of them seem to work. If anyone can help out that'd be great!
The DB part of me is crying because you should really be submitting one query instead of multiple queries. Specifically, if $y is a safe variable, you should have something like this:
$sql = "replace into weight (user_id, column1)
select user_id,
0.4*column1_a+0.6*column1_bon as calc1
from history_summary where user_id < $y";
There are other questions I have, such as if you really want REPLACE or you want UPDATE but that is a problem for another day.
BUT, your question was more about debugging, and it seems that the command(s) are executing successfully. Try running your query on many different values of $y. Perhaps you have encountered an edge case
The question is about debugging, so I'll respond to that, but I double-down on #mankowitz points below.
Debugging
Do you have access to the DB server? Can you watch the query log on the server to confirm that it received what you think the code is sending?
Try the old standby of println style debugging and echo out the SQL string before sending. The <pre> and --- help to make sure you are able to see any extraneous characters you might have in your SQL string:
...
echo "About to send SQL: <pre>--- $findweight ---</pre>\n"
if (mysqli_query($link, $findweight)) {
...
Is this code wrapped inside of a SQL transaction that is not (auto)committed when the script ends?
Do other, perhaps simpler queries work? Have you tried the simple SELECT 1 and echoed out the result?
More arcane and improbable given the context of the question: is fsync disabled on the server? If so, then each mysql_query does not guarantee the DB has actually written the contents to disk yet, and you may eventually see the rows get updated.
What does mysqli_query return? The code currently ignores the result other than checking if it's falsey. Store the result and introspect it.
An edge case of mysqli_query. Ten plus years ago, I encountered cases where mysql_query (note the lack of i) would not return FALSE despite a failing query. It was an oddball case of the connected user not having access to manipulate or view the rows in question, highlighting a bug in mysql_query as well as my connection string.
Database interaction
Be smart about DB queries, minimize the communication, and use bulk operations wherever possible. Your code, as structured could be rewritten in a couple of ways, both of which require only a single call to the database:
The WHERE-IN construct allows you to be very precise about which rows you address:
$ids = [];
for ( $y = 0; $y < $usercount; ++$y )
$ids[] = $y;
$ids = implode(',', $ids);
$findweight = "REPLACE INTO weight (user_id, column1)
SELECT user_id, 0.4*column1_a + 0.6*column1_bon
FROM history_summary WHERE user_id IN ($ids)";
Or, since the IDs the code addresses are contained within a contiguous range, do as #mankowitz suggested, removing the need for a for-loop entirely:
$findweight = "REPLACE INTO weight (user_id, column1)
SELECT user_id, 0.4*column1_a + 0.6*column1_bon
FROM history_summary WHERE user_id BETWEEN 0 AND $usercount";
# alternate form
$findweight = "REPLACE INTO weight (user_id, column1)
SELECT user_id, 0.4*column1_a + 0.6*column1_bon
FROM history_summary WHERE 0 <= user_id AND user_id <= $usercount";
If you absolutely must use individual per-row statements, consider a prepared statement so the DB need only parse and plan the statement once:
$findweight = 'REPLACE INTO weight (user_id, column1)
SELECT user_id, 0.4*column1_a + 0.6*column1_bon
FROM history_summary WHERE user_id = ?';
if ($psth = mysqli_prepare($link, $findweight)) {
$y = 0;
mysqli_stmt_bind_param($psth, 'i', $y);
while ( ++$y < $usercount ) {
if ( mysqli_stmt_execute( $psth ) ) {
echo "Successful REPLACE operation for user_id = $y<br>\n";
}
else {
echo "Error in REPLACE operation for user_id = $y<br>\n";
break;
}
}
}
I need to synchronize specific information between two databases (one mysql, the other a remote hosted SQL Server database) for thousands of rows. When I execute this php file it gets stuck/timeouts after several minutes I guess, so I wonder how I can fix this issue and maybe also optimize the way of "synchronizing" it.
What the code needs to do:
Basically I want to get for every row (= one account) in my database which gets updated - two specific pieces of information (= 2 SELECT queries) from another SQL Server database. Therefore I use a foreach loop which creates 2 SQL queries for each row and afterwards I update those information into 2 columns of this row. We talk about ~10k Rows which needs to run thru this foreach loop.
My idea which may help?
I have heard about things like PDO Transactions which should collect all those queries and sending them afterwards in a package of all SELECT queries, but I have no idea whether I use them correctly or whether they even help in such cases.
This is my current code, which is timing out after few minutes:
// DBH => MSSQL DB | DB => MySQL DB
$dbh->beginTransaction();
// Get all referral IDs which needs to be updated:
$listAccounts = "SELECT * FROM Gifting WHERE refsCompleted <= 100 ORDER BY idGifting ASC";
$ps_listAccounts = $db->prepare($listAccounts);
$ps_listAccounts->execute();
foreach($ps_listAccounts as $row) {
$refid=$row['refId'];
// Refsinserted
$refsInserted = "SELECT count(username) as done FROM accounts WHERE referral='$refid'";
$ps_refsInserted = $dbh->prepare($refsInserted);
$ps_refsInserted->execute();
$row = $ps_refsInserted->fetch();
$refsInserted = $row['done'];
// Refscompleted
$refsCompleted = "SELECT count(username) as done FROM accounts WHERE referral='$refid' AND finished=1";
$ps_refsCompleted = $dbh->prepare($refsCompleted);
$ps_refsCompleted->execute();
$row2 = $ps_refsCompleted->fetch();
$refsCompleted = $row2['done'];
// Update fields for local order db
$updateGifting = "UPDATE Gifting SET refsInserted = :refsInserted, refsCompleted = :refsCompleted WHERE refId = :refId";
$ps_updateGifting = $db->prepare($updateGifting);
$ps_updateGifting->bindParam(':refsInserted', $refsInserted);
$ps_updateGifting->bindParam(':refsCompleted', $refsCompleted);
$ps_updateGifting->bindParam(':refId', $refid);
$ps_updateGifting->execute();
echo "$refid: $refsInserted Refs inserted / $refsCompleted Refs completed<br>";
}
$dbh->commit();
You can do all of that in one query with a correlated sub-query:
UPDATE Gifting
SET
refsInserted=(SELECT COUNT(USERNAME)
FROM accounts
WHERE referral=Gifting.refId),
refsCompleted=(SELECT COUNT(USERNAME)
FROM accounts
WHERE referral=Gifting.refId
AND finished=1)
A correlated sub-query is essentially using a sub-query (query within a query) that references the parent query. So notice that in each of the sub-queries I am referencing the Gifting.refId column in the where clause of each sub-query. While this isn't the best for performance because each of those sub-queries still has to run independent of the other queries, it would perform much better (and likely as good as you are going to get) than what you have there.
Edit:
And just for reference. I don't know if a transaction will help here at all. Typically they are used when you have several queries that depend on each other and to give you a way to rollback if one fails. For example, banking transactions. You don't want the balance to deduct some amount until a purchase has been inserted. And if the purchase fails inserting for some reason, you want to rollback the change to the balance. So when inserting a purchase, you start a transaction, run the update balance query and the insert purchase query and only if both go in correctly and have been validated do you commit to save.
Edit2:
If I were doing this, without doing an export/import this is what I would do. This makes a few assumptions though. First is that you are using a mssql 2008 or newer and second is that the referral id is always a number. I'm also using a temp table that I insert numbers into because you can insert multiple rows easily with a single query and then run a single update query to update the gifting table. This temp table follows the structure CREATE TABLE tempTable (refId int, done int, total int).
//get list of referral accounts
//if you are using one column, only query for one column
$listAccounts = "SELECT DISTINCT refId FROM Gifting WHERE refsCompleted <= 100 ORDER BY idGifting ASC";
$ps_listAccounts = $db->prepare($listAccounts);
$ps_listAccounts->execute();
//loop over and get list of refIds from above.
$refIds = array();
foreach($ps_listAccounts as $row){
$refIds[] = $row['refId'];
}
if(count($refIds) > 0){
//implode into string for use in query below
$refIds = implode(',',$refIds);
//select out total count
$totalCount = "SELECT referral, COUNT(username) AS cnt FROM accounts WHERE referral IN ($refIds) GROUP BY referral";
$ps_totalCounts = $dbh->prepare($totalCount);
$ps_totalCounts->execute();
//add to array of counts
$counts = array();
//loop over total counts
foreach($ps_totalCounts as $row){
//if referral id not found, add it
if(!isset($counts[$row['referral']])){
$counts[$row['referral']] = array('total'=>0,'done'=>0);
}
//add to count
$counts[$row['referral']]['total'] += $row['cnt'];
}
$doneCount = "SELECT referral, COUNT(username) AS cnt FROM accounts WHERE finished=1 AND referral IN ($refIds) GROUP BY referral";
$ps_doneCounts = $dbh->prepare($doneCount);
$ps_doneCounts->execute();
//loop over total counts
foreach($ps_totalCounts as $row){
//if referral id not found, add it
if(!isset($counts[$row['referral']])){
$counts[$row['referral']] = array('total'=>0,'done'=>0);
}
//add to count
$counts[$row['referral']]['done'] += $row['cnt'];
}
//now loop over counts and generate insert queries to a temp table.
//I suggest using a temp table because you can insert multiple rows
//in one query and then the update is one query.
$sqlInsertList = array();
foreach($count as $refId=>$count){
$sqlInsertList[] = "({$refId}, {$count['done']}, {$count['total']})";
}
//clear out the temp table first so we are only inserting new rows
$truncSql = "TRUNCATE TABLE tempTable";
$ps_trunc = $db->prepare($truncSql);
$ps_trunc->execute();
//make insert sql with multiple insert rows
$insertSql = "INSERT INTO tempTable (refId, done, total) VALUES ".implode(',',$sqlInsertList);
//prepare sql for insert into mssql
$ps_insert = $db->prepare($insertSql);
$ps_insert->execute();
//sql to update existing rows
$updateSql = "UPDATE Gifting
SET refsInserted=(SELECT total FROM tempTable WHERE refId=Gifting.refId),
refsCompleted=(SELECT done FROM tempTable WHERE refId=Gifting.refId)
WHERE refId IN (SELECT refId FROM tempTable)
AND refsCompleted <= 100";
$ps_update = $db->prepare($updateSql);
$ps_update->execute();
} else {
echo "There were no reference ids found from \$dbh";
}
Say I have this loop:
foreach ($array as $a) {
if ($a == $b) {
mysql_query("UPDATE table SET this = 'that' WHERE id='$a'");
}
}
And a table...
id this blah
------------------------
1 that 54
2 that 73
3 there 27
Inside that loop, I also want to find the value stored in the tables blah field from the current record that is being updated.
Whats the most effective way to do this?
You can have your query consist of multiple statements, and the last statement is what is used for the "results".
So, you can just add a "select" statement to the end of the update query and treat it like a normal select statement:
UPDATE table SET this = 'that' WHERE id='$a'; SELECT blah from [your table] WHERE id = '$a'
The advantage with this method is that it doesn't require an additional DB call.
Of course, you will want to be escaping the values put into the SQL statements to prevent SQL injection, but that's another matter.
Update
This was my first second SO answer which I felt needed revising. Searching around, I found a much better answer to your question.
From the accepted answer for question: SQL: Update a row and returning a column value with 1 query
You want the OUTPUT clause
UPDATE Items SET Clicks = Clicks + 1
OUTPUT INSERTED.Name
WHERE Id = #Id
Similar question: Is there a way to SELECT and UPDATE rows at the same time?
Old Answer
Add a SELECT statement to the end of your UPDATE query.
mysql_query("UPDATE table SET this = 'that' WHERE id='$a'; SELECT blah WHERE id='$a';");
This prevents you from ensuring the update took place since mysql_query only returns the last statement's result.
You could also write a custom function that performs both statements but, for instance, won't preform the SELECT if the UPDATE failed, etc.
** Skeleton Function - Not Tested **
function MyUpdate($query, $id){
$retVal = "-1" // some default value
$uResult = mysql_query("UPDATE table SET this = 'that' WHERE id='$a'");
if( $uResult )
$result= mysql_query('SELECT blah WHERE id=$a');
if (!$result) {
die('Invalid query: ' . mysql_error());
}
$retVal = $result;
}
return $retVal;
}
I want to fetch the last result in MySQL database table using PHP. How would I go about doing this?
I have 2 Columns in the Table, MessageID(auto) & Message.
I already know how to connect to the database.
Use mysql_query:
<?php
$result = mysql_query('SELECT t.messageid, t.message
FROM TABLE t
ORDER BY t.messageid DESC
LIMIT 1') or die('Invalid query: ' . mysql_error());
//print values to screen
while ($row = mysql_fetch_assoc($result)) {
echo $row['messageid'];
echo $row['message'];
}
// Free the resources associated with the result set
// This is done automatically at the end of the script
mysql_free_result($result);
?>
The SQL query:
SELECT t.messageid, t.message
FROM TABLE t
ORDER BY t.messageid DESC
LIMIT 1
...uses the ORDER BY to set the values so the highest value is the first row in the resultset. The LIMIT says that of all those rows, only the first is actually returned in the resultset. Because messageid is auto-increment, the highest value is the most recent one...
Records in a relational database do not have an intrinsic "order" so you cannot fetch the "last" record without some kind of ORDER BY clause.
Therefore, in order to fetch the "last" record, simply reverse the ORDER BY clause (change ASC to DESC or vice versa) then select the first result.
If you have an auto-increment field and you just want to find the last value that was inserted, you can use the fact that the auto-increment fields are ever-increasing (therefore the "last" one will be the one with the highest value) and do something like this:
SELECT *
FROM my_table
ORDER BY id_field DESC
LIMIT 1
Of course you can select the last row by sorting DESC in your query. But what if you want to select the first row and then the last. You can run a new query, but you can also use the function mysql_data_seek. check code below:
$result = mysql_query('YOUR QUERY') or die('Invalid query: ' . mysql_error());
$row_first = mysql_fetch_array($result);
mysql_data_seek($result , (mysql_num_rows($result)-1));
$row_last = mysql_fetch_array($result);
Hope this helps
The MySql query would look like this:
select MessageID, Message
from Table
order by MessageID desc
limit 1;
I am too rusty with PHP to give you the right syntax for executing this.
This query works because you have an auto-incrementing identifying field (MessageID). By ordering the results by that field in descending (largest to smallest) order we are effectively returning the records in the table in reverse order. The limit 1 clause simply limits the result set to one record - the last one in the table.
What do you mean by "the last result"? You need to precise a bit more.
Do you mean "the last entry I registered"?
In this case you should use the appropriate method (depending on the extension you are using) mysqli->insert_id OR mysql_insert_id.
If you mean "the latest entry in the table", an SQL query such as Andrew Hare's is just what you need.
Do you mean the last record or do you need the id of the most recently inserted record? For that you would use the PHP mysql_insert_id() function. Or if you are using the myusqli extension use $mysqli->insert_id.
for some reason (which I don't know why), my boss force me to get the data in this way:
$message_arr = array();
while ($row = mysql_fetch_assoc($result)) {
$message_arr['messageid']= $row['messageid'];
$message_arr['message']= $row['message'];
}
return $message_arr;
Of course, you need everything from OMG Ponies's answer I'm just telling you another way to do it =)
I hope this help.
You should use SELECT query. How SELECT works.
SELECT * FROM table - selects everything in a table (id, row 1, row 2,...)
SELECT id FROM table - selects only particular row from table.
If you now know, how to select, you can use additional logic.
SELECT * FROM table ORDER by id DESC LIMIT 1;
selects everything from table table, orders it by id - orders it DESCENDING and limits the query to only one result.
If you would do it like this:
SELECT * FROM table ORDER by id ASC limit 1; - you would get 1 entry into database.
You can order it by any row you want.
Hope it helps.
One thing to remember is that data does not get saved in the insertion order in any MYSQL database. So in order to get the last entered record u will have to have an auto increment field. Since there is an auto increment field in this table we are good to go.
The below script will help to get the last entered record
<?php
$sql = "SELECT * FROM table_name ORDER BY MessageID DESC LIMIT 2";
$result_set = mysql_query($sql);
if($result_set){
while($row = mysql_fetch_array($result_set)) {
echo "Message Id: ".$row['MessageID']."<br>";
echo "Message: ".$row['Message']."<br>";
}
//creating alert
echo "<script type=\"text/javascript\">alert('Data was Retrieved
successfully');</script>";
}
else{
//creating alert
echo "<script type=\"text/javascript\">alert('ERROR! Could Not Retrieve
Data');</script>";
}
?>
The query selects all the records in the table and orders them according to the descending order of the MessageID (as it is the auto increment field) and limits the returned result to only one record. So since the table is ordered according to the descending order of the MessageID only the last entered record will be returned.
NOTE: if you are using a newer version you will have to use mysqli_query($connection_variable,$sql); instead of mysql_query($sql); and mysqli_fetch_array($result_set) instead of mysql_fetch_array($result_set)
$result = mysql_query('select max(id) from your_table ') or die('Invalid query: ' . mysql_error());
while ($row = mysql_fetch_assoc($result)) {
echo $row['id'];
echo $row['message'];
}
//
//
mysql_free_result($result);
simple like that
this code of php works fine
SELECT t.messageid, t.message
FROM TABLE t
ORDER BY t.messageid DESC
LIMIT 1
if you don't have concurrent entries going into some table.b'cause concurrent entries may not go in accordance of their insertion order.
$statement = $PDO->prepare("
SELECT MessageID,
Message
FROM myTable
ORDER BY MessageID DESC
LIMIT 1;
");
$statement->execute();
$result = $statement->fetch(\PDO::FETCH_ASSOC);
echo $result['MessageID']." and ".$result['Message'];