ON DUPLICATE KEY UPDATE - Condition WHERE vs CASE WHEN vs IF? - php

I am refering to this post. I am stuck with a problem I can't resolve. I try to insert multiple rows with a php script into a MySQL database. I don't succeed in updating the whole thing using ON DUPLICATE KEY UPDATE and using a WHERE condition (at the end of the code below) I would like to use to update only an entry has been modified recently:
// for information (used in a foreach loop):
$args[] = '("'.$row['lastname'].'", '.$row['phone'].', "'.$row['lastModification'].'")';
// then:
$stringImplode = implode(',', $args);
// Where I am stuck - WHERE statement:
$sql = $mysqli->query('INSERT INTO table_name '. (lastname, phone, timestamp) .' VALUES '.$stringImplode .'ON DUPLICATE KEY UPDATE lastname=VALUES(lastname), phone=VALUES(phone) WHERE timestamp > VALUES(lastModification);
Everything works fine except I cannot set any WHERE condition at this point that involves multiples entries. Maybe the WHERE statement in this case is not intended to refer to a condition in this statement.
I was told to try with a database procedure using a JOIN statement and a temporary table with first all my entries and then querying some conditions. But I have to admit I don't understand very well how I could leverage such a table to update an other table.
Is there an easy and lovely way to use a "CASE WHEN" or an "IF" statement in this case?
Would something like
INSERT INTO ... ON KEY DUPLICATE UPDATE lastname = VALUES(lastname), phone = VALUES(phone)
CASE WHEN (timestamp > VALUES(lastModification)) THEN do nothing ...
or
...ON KEY DUPLICATE UPDATE... IF (timestamp > VALUES(lastModification)) ...
If anyone could help me, I would be very grateful.
EDIT: Since I will have many variables, could it be used in this way:
INSERT INTO ... ON KEY DUPLICATE UPDATE
IF(timestamp > VALUES(timestamp),
(
name = VALUES(name),
number = VALUES(number),
timestamp = VALUES(timestamp)
....many other variables
),
(
name = name,
number = number,
timestamp = timestamp
....many other variables)
)

You can use simple IF function in value like this:
INSERT INTO ... ON KEY DUPLICATE UPDATE
name = VALUES(name),
number = VALUES(number),
timestamp = IF(timestamp > VALUES(timestamp), VALUES(timestamp), timestamp)
If condition is not met, it will update timestamp with the same timestamp which already exists. It does not matter, because update to same values is optimized before it is even executed, so MySQL will not make real update. You should not afraid of some performance penalty.
EDIT:
IF works likes this:
IF(condition, returned when true, returned when false)
Maybe you need to switch those two arguments to fit your condition like you want.

Related

Is it possible and safe to combine MySQL syntax for INSERT INTO SET ánd ON DUPLICATE KEY UPDATE (with the same column set)?

I know how the specific MySQL query syntax for INSERT INTO SET and ON DUPLICATE KEY UPDATE work. But, can you combine them? And... can you use the same $columnSet string (see below) for the INSERT and the UPDATE part, leaving the unique field in there?
Concider this example (I use php with pdo on my server):
// Define the query
$columnSet = "id=:id, date=:date, created=:created, modified=:modified, removed=:removed, synced=1, points=:points, comment=:comment";
$insertQuery = "INSERT INTO point_events SET"
. $columnSet." ON DUPLICATE KEY UPDATE ".$columnSet;
// Define the data
$pointData['id'] = 'someuniqueid not auto incremented';
$pointData['created'] = 1234; // a timestamp
$pointData['modified'] = 1234; // a timestamp
$pointData['removed'] = NULL;
$pointData['points'] = 10;
$pointData['comment'] = "A comment";
$pointData['date'] = time();
// Execute the query. pdoModify is custom function that executes the query.
pdoModify($pdoNew, $insertQuery, $oldData);
All posts I've seen do not combine these two syntaxes and all posts on ON DUPLICATE KEY UPDATE seem to suggest that you should not include the (duplicate) key in the string after UPDATE. So, is this safe, or could this behave differently / have unwanted side effects. Specifically leaving the unique id in the $columnSet for the UPDATE is what concerns me.
I believe that your code should work. Typically, since the duplicate id unique key is initially checked, it is guaranteed than it will left unchanged when the ON DUPLICATE KEY clause is invoked.
However, this behavior is suboptimal (you are updating fields that are unchanged), and alos quite rigid: what if you need to increase the amount with the newly inserted value, or to concatenate the new comment at the end of thte existing one?
As commented by Rogue, MySQL syntax has a nice feature that allows the use of VALUES(<some column>) in the ON DUPLICATE KEY clause to refer to a value that was passed for INSERT. I would suggest to use that, since it is more elegant, and gives you much more control and flexibility.
Example:
$insertQuery =
"INSERT INTO point_events SET"
. $columnSet
." ON DUPLICATE KEY UPDATE"
." modified = VALUES(modified)" # replace this value
.", synced = 1" # assign a fixed value
.", amount = amount + VALUES(amount)" # add the new value
.", comment = CONCAT(comment, ', ', VALUES(comment))" # concatenante the new value

PHP MYSQL Update query

I currently have the following update statement but is there anyway that I can make it retain the current values but insert and new values that are not in the db?
If not what would be the best way to achieve this?
UPDATE INTO {refocus_candidate_category} SET canid=?, categoryid=? WHERE canid=? AND categoryid=?",array($emailCheck['id'], $id, $emailCheck['id'], $id));
Function:
$catParams = array_merge(array($emailCheck['id']), $fields['Occupation']);
$catPlaceholders = '?'.str_repeat(',?',count($fields['Occupation'])-1);
$catCheck = CMS::selectQuery("SELECT * FROM {table} WHERE canid=? AND categoryid IN (".$catPlaceholders.")", $catParams);
if($catCheck != FALSE)
{
for($i=0; $i<count($fields['Occupation']); $i++) {
$id = $fields['Occupation'][$i];
CMS::updateQuery("UPDATE INTO {table} SET canid=?, categoryid=? WHERE canid=? AND categoryid=?",array($emailCheck['id'], $id, $emailCheck['id'], $id));
}
echo 'found update';
}
ID Print
$fields['Occupation'][$i] = 1678
It's not clear to me from your question precisely what you mean, but there are a number of alternatives for inserts/updates that deal with missing or already present values.
Firstly, if you just want to insert into mysql and have it either create a new row or replace an existing row (where existing is determined by the primary key matching) use REPLACE INTO instead of INSERT INTO. REPLACE INTO tries an insert, but if the primary key already exists, it turns the query into a DELETE and then retries the INSERT.
If you want to insert a new row but leave an existing row alone if you've already got one, you can either use INSERT IGNORE INTO (which may also fail to insert if you've got your data types or column info wrong...) or INSERT INTO ... ON DUPLICATE KEY UPDATE which allows you to do much finer grained control of how you handle inserts of items that already exist.
There's other options as well, but those are probably the most relevant.

mysql_affected_rows(); does not work for checking if row exists

i am using mysql_affected_rows() to check if i have to enter new record or update existing, but the problem is if the user tries to enter exactly same data as record which already exists it runs insert into.
$result = mysql_query("update Data set Score='$score',Comment='$_POST[Comments]' where Date='$_POST[forDay_3]-$_POST[forDay_1]-$_POST[forDay_2]' AND User='$_POST[UserID]';");
$last = mysql_affected_rows();
if ($last==0) {
$result1 = mysql_query("INSERT INTO Data (User,Date,Score,Comment) VALUES ('$_POST[UserID]','$_POST[forDay_3]-$_POST[forDay_1]-$_POST[forDay_2]','$score','$_POST[Comments]')");
what should i do to avoid redundant entries
You could parse mysql_info() output (but the solution itself will be affected by race condition issue)
You could create unique key User + Date and end up with a single query using ON DUPLICATE KEY UPDATE syntax:
INSERT INTO `Data` (User,Date,Score,Comment)
('$_POST[UserID]','$_POST[forDay_3]-$_POST[forDay_1]-$_POST[forDay_2]','$score','$_POST[Comments]')
ON DUPLICATE KEY UPDATE Score='$score',Comment='$_POST[Comments]'
some solutions:
add another query to see if data exists, and then decide if you want to do some action (update/delete) or nothing.
add a 'modified' column with type "TIMESTAMP" and make it on update - CURRENT_TIMESTAMP
i'd go with first option.
btw, you should escape your post data (mysql_real_escape_string) to prevent injects or malformed query string
You may get the number of affected rows with FOUND_ROWS() instead of mysql_affected_rows(). The latter counts the not modified rows as well.
$result = mysql_query("update Data set Score='$score',Comment='$_POST[Comments]' where Date='$_POST[forDay_3]-$_POST[forDay_1]-$_POST[forDay_2]' AND User='$_POST[UserID]';");
$last = mysql_query("SELECT ROW_COUNT();");
$last = mysql_fetch_array($last);
...
Reference: http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_row-count

Unique constraint, how to avoid duplicates

According to my previous Query that post i have a table that looks like this:
|| *nid* || *language* ||
|| 8 || Chinese ||
|| 8 || Portuguese ||
|| 8 || German |
In which 'nid' and 'language' have a unique constraint.
With this setup how can i make sure that the there wont be any duplicate when i try to insert a new row ?
EDITED
I am guessing I should try to make a query such as:
SELECT * FROM lang WHERE nid = $nid AND language = $lang
If this return FALSE then i know i can now insert my data. Is this correct ?
Enforce the unique constraint by creating a unique key:
ALTER TABLE the_table
ADD UNIQUE INDEX nid_language_unique (nid, language);
This constraint forbid two rows having the same nid and language.
Any query attempting to violate the constraint will fail.
As you want to ignore errors (and still abort the query), you can use INSERT IGNORE and UPDATE IGNORE:
INSERT IGNORE INTO the_table (nid, language) VALUES (8, 'Chinese')
/* row not inserted and no error */
If you have actually established a unique constraint in the database then MySQL will not let you insert the second row. The statement will raise an exception. You can trap and ignore that in your code. If you're not interested in whether the row was added or not, you can use the IGNORE keyward in the MySQL INSERT INTO command and the row will either be added (if not there) or the command will complete without an error.
SELECT nid, language FROM lang WHERE nid = $nid AND language = $lang
If this return FALSE then i know i can now insert my data. Is this correct ?
Yes, but you need to write:
$nid = mysql_real_escape_string($_POST['nid']);
$lang = mysql_real_escape_string($_POST['lang']);
$query = SELECT nid, language FROM lang WHERE nid = '$nid' AND language = '$lang'
// notice the quotes ^ ^ ^ ^
If you forget these your query give an error (and be at risk from SQL-injection).
If you have a unique constraint, you can just go ahead and insert the data, because MySQL will do the above test for you.
You can use a counter in your code (not SQL, but the one you use to use SQL, like PHP, or else)
You can use MySQL max function and add one (like max(nid)+1 (but don't remember about MySQL's max function))
You can use a random number with 10 characters (so you'd would have a really really low risk to go into an error)
I used the first and last way many times.
And if you want to be sure that you won't have a duplicate, use the solutions from your last posts. Stuff like UNIQUE constraint will prevent you to insert twice the same nid or language (thus, if you don't handle it, your program will crash).

php incrementing while loop

$blanknumber = $_POST["blankstartnumber"];
while ($blanknumber <= ($_POST["blankendnumber"] ))
{
echo "$blanknumber";
$blankid = $blanknumber;
$query = "INSERT INTO blank (Blank_ID) VALUES ('$blankid')";
mysql_query($query,$con);
$blanknumber++;
}
So the values are added into the database. Lets say if I have the starting number at 1 and ending at 5. It will all the those values, but it's still trying to add more into the database. I also tried adding an IF statement aswell. if ($blanknumber != $_POST["blankendnumber"])
12345 Error: Duplicate entry '5' for
key 'PRIMARY'
Make sure your $POST value is an integer; by default, I believe it will be cast as a string.
$_POST['varName'] = (int) $_POST['varName'];
edit:
$blanknumber = $_POST["blankstartnumber"];
while ($blanknumber <= ($_POST["blankendnumber"] ))
This should only execute once, since you're setting both comparison variables equal. Definitely 2x check your code.
The database error indicates that Blank_ID is your primary key for that table, and you'd already inserted a 5 into the row. A primary key's values can exist only once in the entire table - duplicates are forbidden (if they were allowed, it wouldn't be a primary key anymore).
If your while loop isn't ending, I'd suggest dumping out both the blankendnumber and blankstartnumber before the loop starts, making sure you've got the right values in there.
It looks like it's actually functioning properly, but you might not have tidy'ed up your db table prior to running. If your output was:
123455 Error: Duplicate entry '5'...
Then, you'd have a programming error, as 5 is getting run twice. Instead, I think you already have data in the blank table that causes a conflict.
Edit: to automatically have MySQL handle the duplicate key error gracefully, you can use the ON DUPLICATE KEY clause to update the row.
INSERT INTO blank (Blank_ID) VALUES (5) ON DUPLICATE KEY UPDATE mod_date = NOW();

Categories