UPSERT using InnoDB engine? - php

Here is the solution for an UPSERT that uses primary key idfill to check for duplicates. I'm just not sure if it's sql injection proof or even efficient?
$idq="SELECT idafill FROM afillInfo, actorsInfo
WHERE (actorsInfo.id = afillInfo.id_actor) AND email = '$_SESSION[email]'" or die (mysql_error());
$sql = "INSERT INTO afillInfo (idfill, agency, agentPhone, afillChoice, id_actor)
VALUES ( ?,?,?,?, ( select id FROM actorsInfo WHERE email = ?))
ON DUPLICATE KEY UPDATE
`id_actor` = VALUES(`id_actor`),
`agency` = VALUES(`agency`),
`agentPhone` = VALUES(`agentPhone`),
`afillChoice` = VALUES(`afillChoice`)
";
if (($stmt = $con->prepare($sql)) === false) {
trigger_error($con->error, E_USER_ERROR);
}
$result= mysqli_query($con, $idq);
$row_number = 1;
while ($row = mysqli_fetch_array($result)) {
$idfill= $row["idafill"];
}
if ($stmt->bind_param("sssss",
$idfill,
$_POST["agency"], $_POST["agentPhone"],
$_POST["afillChoice"], $_SESSION["email"]) === false) {
trigger_error($stmt->error, E_USER_ERROR);
}
if (($stmt->execute()) === false) {
trigger_error($stmt->error, E_USER_ERROR);
}

INSERT adds a new row if it can.
When you use INSERT...ON DUPLICATE KEY UPDATE, it performs an UPDATE only if your INSERT would create a duplicate value in a primary key or unique key column.
Thank you for posting your table definition. I see now that you have no UNIQUE column besides idfill, the primary key.
So if you don't specify a value for idfill, it'll generate a new value in a new row. There's no way this will trigger the duplicate key. It makes no sense to run the query as you are doing and not expect it to create a new row.
You must specify an existing value in your INSERT statement for a PRIMARY or UNIQUE KEY, in order to cause the INSERT to fail and fall through to do the UPDATE. Otherwise the INSERT will succeed, by creating a new row with a distinct value for the primary key.
So you must add the idfill column to your INSERT, and specify a value that conflicts with one already existing in the database.
INSERT INTO afillInfo (idfill, agency, agentPhone, afillChoice, id_actor)
VALUES (?, ?, ?, ?, ( SELECT id FROM actorsInfo WHERE email = ?))
...
Apologies that I didn't notice this immediately, but another problem is that the UPDATE part of your statement isn't changing anything.
... UPDATE
`id_actor` = `id_actor`,
`agency` = `agency`,
`agentPhone` = `agentPhone`,
`afillChoice` = `afillChoice`
This sets the columns to exactly the same values they had before. It's a no-op. It's the equivalent of doing this in PHP:
$sql = $sql;
You can work around this by using the VALUES() function to re-use the values you tried to insert. Here's an example:
... UPDATE
`id_actor` = VALUES(`id_actor`),
`agency` = VALUES(`agency`),
`agentPhone` = VALUES(`agentPhone`),
`afillChoice` = VALUES(`afillChoice`)

Related

MySQL INSERT --- get the ID (auto_increment) IF one value is UNIQUE

Is there a better or faster way to return the ID?
The column customer is unique
$inserted_id = null;
if( !$mysqli->query("INSERT INTO users (id,customer) VALUES(null,'foo')" ){
// Is it possible to avoid this 2nd query?
$result = $mysqli->query("SELECT id FROM users WHERE customer='foo'");
$inserted_id = $result->fetch_assoc()['id'];
} else {
$inserted_id = $mysqli->insert_id;
}
Use $last_id = $mysqli->insert_id(); after your insert. That get's the last generated auto increment. Your code as it stands will not be accurate. Updated for Object Oriented perspective.
You can modify the insert query to UPDATE auto incremented column when a duplicate record is attempted to INSERT.
INSERT INTO users (id,customer)
VALUES (null,'foo')
ON DUPLICATE KEY UPDATE
id = LAST_INSERT_ID(id)
id = LAST_INSERT_ID(id) will return the value of the AUTOINCREMENT column for the last INSERT and set the value for mysqli_insert_id. This will make last insert id available during all inserts.
Have modified your code:
$inserted_id = null;
if($mysqli->query("INSERT INTO users (id,customer) VALUES(null,'foo') ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id)" )
{
$inserted_id = $mysqli->insert_id; // Will return last insert ID in case of successful inserts as well as failed inserts due to duplicate key
}

SQL INSERT INTO table results in duplicates

I have a script that I have setup a CRON for that is getting values from a 3rd party server via JSON (cURL)
Right now every time the cron runs it will INSERT a completely new record. Causing duplicates, and resulting me in manually removing the dups.
How would I go about preventing duplicates, and only update the information that is either missing, or different from the NEW $VAR values?
What I want to do can be expressed like this: IF old value is NOT new value use new value else use old value;
if ($stmt->num_rows !== 1) {
if ($insert_stmt = $mysqli->prepare("
INSERT INTO members (
start_date
)
VALUES (?)"))
{
$insert_stmt->bind_param('s',
$StartDate,
);
if (! $insert_stmt->execute()) { echo ''; }
}
}
}
You should try using INSERT ... ON DUPLICATE KEY UPDATE. Documentation
This does mean that you will have to define some unique (could be primary) key to the table that is always constant so MySQL knows what to update.
A quick example of how you would do it:
INSERT INTO table (f1,f2,f3) VALUES ('something_unique',2,5)
ON DUPLICATE KEY UPDATE f2=2,f3=5
The following statement will be silently ignored if one of the fields with the flags UNIQUE or PRIMARY KEY already exist in the database. If you're searching for INSERT IF NOT EXISTS this is probably what you're looking for:
INSERT IGNORE INTO `members` SET name='Steve',start_date='2015-11-20';
You can also overwrite a record that already exists using REPLACE. If it doesn't yet exist, it will be created:
REPLACE INTO `members` SET name='Steve',start_date='2015-11-20';
Another thing to consider would be INSERT ... ON DUPLICATE KEY UPDATE syntax:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
UPDATE table SET c=c+1 WHERE a=1;
I ended up writing another if statement to check if a unique value existed from incoming and the existing db value existed and leaving it blank to prevent it from importing duplicates. I also wrote a separate file to update where values differentiate between what I am receiving as (new) and what is in the database (old) which actually worked out great for my application.
Here is my answer for anyone else that runs into this issue :)
$prep_stmt = "SELECT * FROM table WHERE column_keys=?";
$stmt = $mysqli->prepare($prep_stmt);
if ($stmt) {
$stmt->bind_param('s',$varvalues);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows == 1) {
if ($insert_stmt = $mysqli->prepare("")) {
$insert_stmt->bind_param('');
if (! $insert_stmt->execute()) {
echo 'shits broke'; }
}
}
else { if ($insert_stmt = $mysqli->prepare("
INSERT INTO table (column_keys)
VALUES (?)")) // you will need a ? per column seperate by a , (?,?,?...?)
{ $insert_stmt->bind_param('s',
$varvalues
); // you will also need to bind a 's' (string) 'i' for num, etc per $var value.
if (! $insert_stmt->execute()) { echo 'shits broke';} //lol
}
}
}
Also a simple error reporting trick I stumbled upon that helped me clean up a few things I overlooked. Just place it at the top of the file, or above you want to debug ;)
error_reporting(E_ALL);

Get ID of record when mysql returns duplicate error

Is there a way to retrieve the ID of a record (primary key) after an insert when the mysql error returns a duplicate key?
E.G. How I would go about it:
$sql = "INSERT INTO table (`col1`, `col2`) VALUES ('$val1', '$val2')";
$result = mysql_query($sql);
if($result){
$id = mysql_insert_id();
}
else {
if(stristr(mysql_error(), "duplicate"){
$sql = "SELECT `id` FROM `table` WHERE `col1`='$val1' AND `col2`='$val2'";
$result = mysql_query($sql);
$row = mysql_fetch_array($result);
$id = $row['id'];
}
else {
die(mysql_error());
}
}
Here I've had to do two sql statements which not only take time and effort, but duplicate code as well.
I cannot use ON DUPLICATE KEY UPDATE because I want to update a different table using the either the last inserted id, or the id of the record that cannot be duplicated.
So, am I right in what I'm doing? Or is there a way to get the id of the row?
Thanks
MySQL will not tell you which record holds the original value, you'll have to find out yourself. Here you are some tips:
Looking for the duplicate substring in the text of the error message does not look very robust. You can just test the value of mysql_errno() against the code for duplicate entry, which is 1062 (you can find all codes in the manual).
The mysql extension does not provide a mechanism to find out name of the violated key, so you'll have to use the non-robust approach of parsing the text of the error message:
if( preg_match("/Duplicate entry '.*' for key '(.*)'/Ui", mysql_error(), $matches) ){
$violated_key = $matches[1];
}else{
throw new Exception('Could not find violated key name');
}
Alternatively, just run a previous query (there's no reason to avoid it):
SELECT id
FROM table
WHERE col1=... AND col2=...
FOR UPDATE
The FOR UPDATE clause will lock matching rows to avoid race conditions (assuming InnoDB).

PHP & MYSQL Is this MySQL Syntax correct for UPDATE/INSERT IF EXISTS

My plan is to update/insert existing/new records into a database from an external, i have the information set to go into the database, but cannot check if it exists already.
my first attempted at doing database stuff
$link = resdb::connect();
$q = "IF ( EXISTS ( SELECT * FROM property AS this WHERE this.name = $propertyname ) )"
."begin"
// UPDATE PROPERTY IF IT EXISTS
."UPDATE property (name, propertylocation, propertyrating, propertytype)"
."SET ($propertyname, $locationid, $ratingid, $typeid)"
."WHERE name = $propertyname"
."end"
."ELSE"
."begin"
."INSERT INTO property (name, propertylocation, propertyrating, propertytype)"
."VALUES ($propertyname, $locationid, $ratingid, $typeid)"
."end";
$r = mysqli_query($link, $q);
if($r > 0){
return true;
}
A unique id is made on completion of each row.
First create a unique index on the name column:
ALTER TABLE property ADD UNIQUE KEY(name)
Now you can use the MySql INSERT ... ON DUPLICATE KEY UPDATE...
INSERT property (name, propertylocation, propertyrating, propertytype)
VALUES ($propertyname, $locationid, $ratingid, $typeid)
ON DUPLICATE KEY UPDATE
propertylocation = VALUES(propertylocation),
propertyrating = VALUES(propertyrating),
propertytype = VALUES(propertytype)
Docs: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
Don't forget to sanitize database inputs.

Check if exists, if so, update by 1++, if not insert

Hey guys quick question, I currently have an insert statement
$query= "INSERT into new_mail VALUES ('$to1', '0')"; where fields are username, and message_number
Currently what I would do to check if the entry exists, is do a select query then check the number of rows with mysql_num_rows (php). If rows==1 then I get the current message_number and set it equal to
$row['message_number']+1.
Then I update that entry with another query.
Is there an easier way to do all this in just mysql with just one query (check if exists, if not insert, if so update message_number, increase by 1)?
Depending on how your table is structured, you may be able to use the ON DUPLICATE KEY UPDATE (link to the MySQL manual) feature of INSERT:
INSERT into new_mail VALUES ('$to1', '0') ON DUPLICATE KEY UPDATE message_number=message_number+1
Use INSERT...ON DUPLICATE KEY UPDATE. The MySQL manual has an example which does almost exactly what you need:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
To make this work you need to add a UNIQUE index on the column that you use to check for duplicates. There is one important warning though:
In general, you should try to avoid using an ON DUPLICATE KEY clause on tables with multiple unique indexes.
Got a little confused by your question and your table structures but I think you want something like this.
INSERT INTO new_mail (username, message_number)
VALUES ($username, $message_number)
ON DUPLICATE KEY UPDATE message_number=message_number + 1;
This is presuming username is your primary key (more likely something like userid). Hope this helps.
EDIT: The ON DUPLICATE KEY UPDATE answers are better, but you could do this (eludes the select query):
Assuming you're using the mysqli extenson:
$db = //Some construction of mysqli object;
$sql = 'UPDATE tablename SET RowValue = RowValue + 1 WHERE message_number = ?';
$updateStatement = $db->prepare($sql);
$updateStatement->bind_param('i', $message_number);
$message_number = //Set message number;
$updateStatement->execute();
if ($updateStatement->affectedRows == 0) {
$sql = 'INSERT INTO tablename (RowValue, message_number) VALUES (?, ?)';
$insertStatement = $db->prepare($sql);
$insertStatement->bind_param('ii', $rowValue, $messageNumber);
$rowValue = something;
$messageNumber = something;
$insertStatement->execute();
}

Categories