PHP/SQL Update - update only if value has been change
I have following PHP loop + SQL Update query.
for ($i=0;$i<count($_POST['id']);$i++) {
if(!isset($_POST['live'][$i])){
$_POST['live'][$i] = "0";
} else { $_POST['live'][$i] = "1"; }
$id = ($_POST['id'][$i]);
$live = ($_POST['live'][$i]);
$usr2 = $_SE开发者_运维问答SSION['usr'];
$updated = date("F j, Y, g:i a",time()+60*60);
$sql = "UPDATE news SET live = '$live', usr2 = '$usr2', updated = '$updated' WHERE id = $id";
$result = mysql_query($sql);
//echo $sql."<br />";
}
if($result) {
header("location: notes.php");
exit();
}else {
die("Query failed");
}
How does it work:
- I'm submitting big form will ALL OF THE table rows.
- receiving this in different file as an array
- if
$_POST['live']
is 'not set' - set it to '0', if 'set' set it to 1 - update array data within for loop
How to UPDATE only the rows which have been actually been changed?
Those which value from $_POST['live'] is actually different from this saved in DB, as the condition would be change of our $live row
.
I guess you're concerned about the updated
field and that this value only changes when something has been altered. (If that's not the case forget about this answer.)
You can define an ON UPDATE CURRENT_TIMESTAMP
clause for a timestamp field. Each time a record is updated without explicitly setting a value for this field mysql uses the current time as its new value...
...but only if the record is altered; if you "update" the fields with the same value as are already in that record nothing happens.
demo script:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'localonly', 'localonly');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
setup($pdo);
$stmt = $pdo->prepare('UPDATE soNews SET somevalue=:v WHERE id=:id');
show('original', $pdo);
$stmt->execute( array(':id'=>1, ':v'=>'z') );
show('update with new=old & id=1', $pdo);
$stmt->execute( array(':id'=>2, ':v'=>'y') ); // new value=old value
show('update with new!=old & id=2', $pdo);
function setup($pdo) {
$pdo->exec('
CREATE TEMPORARY TABLE soNews (
id int auto_increment,
somevalue varchar(32),
updated TIMESTAMP DEFAULT 0 ON UPDATE CURRENT_TIMESTAMP,
primary key(id)
)
');
$pdo->exec("INSERT INTO soNews (id,somevalue,updated) VALUES (1,'x', Now()-interval 47 hour),(2,'y', Now()-interval 47 hour)");
}
function show($label, $pdo) {
echo "------ $label --------\n";
foreach( $pdo->query('SELECT * FROM soNews', PDO::FETCH_ASSOC) as $row ) {
echo join(', ', $row), "\n";
}
}
prints
------ original --------
1, x, 2011-08-16 14:09:53
2, y, 2011-08-16 14:09:53
------ update with new=old & id=1 --------
1, z, 2011-08-18 13:09:53
2, y, 2011-08-16 14:09:53
------ update with new!=old & id=2 --------
1, z, 2011-08-18 13:09:53
2, y, 2011-08-16 14:09:53
As you can see as a result of the first update query the timestamp field has been updated while the second query setting new=old didn't affect the updated
field.
Bobby tables will destroy your database. All your bits are belong to him (strictly speaking, this is an exaggeration, but you need to wrap all of your db inputs with mysql_real_escape_string
or better yet, move to PDO's or MySQLi).
Long and the short? No, there is no reliable way to determine whether user input is the same as what is in the database without actually querying the database first or somehow storing the original output from the DB locally ($_SESSION
or whatnot).
There are legitimate use cases for that, but it looks like you're better off just calling the updates. You can prevent them slightly by adding AND LIVE != '$live' AND UR2 != '$ur2'
, but you'll still need to run that many queries.
BTW -- I generally advise people not to use traditional for loops in PHP pretty much ever. PHP's foreach
is better in almost every way. Instead of for ($i=0;$i<count();$i++)
, use foreach( $_POST['id'] as $i => $id )
. You'll already have $id
declared.
Actually, I think that a good way of doing is to:
1) Perform a query to get the old record from the db, then store the row contents in an associative array, with column names as keys.
2) Create a new array by checking the content of each "column" to be updated. If the content received is different from the value stored on the db, update the record data, else ignore and go ahead. Finally send back the updated data to the db with an UPDATE
function updateRecord(array $input_data) {
// Get the data associated to the record id we want to update.
$record_data = $yourDBWrapperClass
->where("id",$_GET['record_id'])
->get(TABLE_NAME);
// Process column by column and append data to the final array.
$final_data = [];
$ignored_columns = ['id', 'last_update'];
foreach ($record_data as $col_name => $value) {
// Update the record data, only when input data shows a difference
if(array_key_exists($col_name, $input_data)
&&
$record_data[$col_name] != $input_data[$col_name])
{
$final_data[$col_name] = $inputData[$col_name];
}
else if (array_key_exist($ignored_columns, $col_name) == FALSE && $record_data[$col_name] == $input_data[$col_name]
{
$final_data[$col_name] == $value;
}
}
// Finally, perform the db update.
$update_result = $yourDBWrapperClass
->where("id", $_GET['record_id'])
->update(TABLE_NAME, $final_data);
return $update_result ? "Record update success" : "Record update failed";
}
note: You don't need to send back the id, or last_update columns: their value is calculated automatically by the server. Sending a wrong value, will cause an error, or provide a wrong information. Think about the last_update column: it's better to leave to MySQL, which will call use the column default to get the value: NOW(); or CURDATE();
expected aspect of variables/arrays
$_GET['record_id'] = 123;
$record_data = [
"id" => 123,
"name" => "John",
"surname" => "Dahlback",
"age" => 31,
"last_update" => "2019-01-01 10:00:00"
];
$input_data = [
"age" => 32
];
$final_data = [
// "id" => 123,
"name" => "John",
"surname" => "Dahlback",
"age" => 32,
// "last_update" => CURDATE();
];
精彩评论