How can I use ON DUPLICATE KEY UPDATE in PDO with mysql? - mysql

DETAILS
I am doing a single insert for the expiry of a new or renewed licence. The time period for the expiry is 2 years from the insertion date.
If a duplicate is detected, the entry will be updated such that the expiry equals the remaining expiry plus 2 years.
Regarding duplicates, in the example below there should only be one row containing user_id =55 and licence=commercial.
TABLE: licence_expiry
--------------------------------------------------------
| user_id | licence | expiry |
--------------------------------------------------------
| 55 | commercial | 2013-07-04 05:13:48 |
---------------------------------------------------------
user_id (int11), licence (varchan50), expiry (DATETIME)
I think in mysql you would write it something like this (Please note that I haven't checked whether the code works in mysql. )
INSERT INTO `licence_expiry`
(`user_id`, `licence`, `expiry`)
VALUES
(55, commercial, NOW()+ INTERVAL 2 YEAR)
ON DUPLICATE KEY UPDATE
`expiry` = `expiry` + INTERVAL 2 YEAR
QUESTION: How can I do this with PDO? I've written a rough outline of what I think I will use, but I'm not sure what to write for the expiry value for the ON DUPLICATE KEY UPDATE.
$sql = "INSERT INTO $table (user_id, licence, expiry)
VALUES (
:user_id,
:licence,
:expiry)
ON DUPLICATE KEY UPDATE expiry = Something";
try {
$dbh = new PDO('login info here');
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$stmt = $dbh->prepare($sql);
$stmt->bindParam(':user_id', $userID, PDO::PARAM_INT);
$stmt->bindParam(':licence',$licence, PDO::PARAM_STR);
$stmt->bindParam(':expiry',$expiry, PDO::PARAM_STR);
$stmt->execute();
//$stmt->closeCursor(); //use this instead of $dbh = null if you will continue with another DB function
$dbh = null;
}
catch(PDOException $e)
{
$error=$e->getMessage();
}
Any help is much appreciated.

You can use MySQL's VALUES() function:
In an INSERT ... ON DUPLICATE KEY UPDATE statement, you can use the VALUES(col_name) function in the UPDATE clause to refer to column values from the INSERT portion of the statement. In other words, VALUES(col_name) in the UPDATE clause refers to the value of col_name that would be inserted, had no duplicate-key conflict occurred.
Therefore, in your case:
ON DUPLICATE KEY UPDATE expiry = VALUES(expiry)
Alternatively, you can create a fourth parameter to which you bind $expiry again:
$sql = "INSERT INTO $table (user_id, licence, expiry)
VALUES (
:user_id,
:licence,
:expiry)
ON DUPLICATE KEY UPDATE expiry = :another";
try {
$dbh = new PDO('login info here');
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$stmt = $dbh->prepare($sql);
$stmt->bindParam(':user_id', $userID , PDO::PARAM_INT);
$stmt->bindParam(':licence', $licence, PDO::PARAM_STR);
$stmt->bindParam(':expiry' , $expiry , PDO::PARAM_STR);
$stmt->bindParam(':another', $expiry , PDO::PARAM_STR);
$stmt->execute();
// etc.

I know you have answer below, but i had same problem and my solution looks quite different but it works for me so if you want to use different statement of using insert in mysql with explicit binding values to columns you can try this code
$sql = "
INSERT INTO
$table
SET
user_id = :user_id,
licence = :licence,
expiry = :expiry
ON DUPLICATE KEY UPDATE
expiry = :expiry
";
$dbh = new PDO('login info here');
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$stmt = $dbh->prepare($sql);
$stmt->bindValue('user_id', $userID , PDO::PARAM_INT);
$stmt->bindValue('licence', $licence, PDO::PARAM_STR);
$stmt->bindValue('expiry' , $expiry , PDO::PARAM_STR);

Related

PDO Mysql same query but insert to multiple users [duplicate]

This question already has answers here:
What is the best way to insert multiple rows in PHP PDO MYSQL?
(4 answers)
Closed last year.
I would like through pdo insert multiple (bulk) rows with same value, only diffent is the user_id
I'm passing a array with userIds but i have no idea how to bind them.
<?php
require_once("db.php");
$usersId = $jsonData["usersId"];
$text = $jsonData["text"];
// Try to fetch the user from the database
$query = "INSERT INTO posts (user_id, text) VALUES (:usersId, :text)";
$stmt = $db->prepare($query);
// Bind value
$stmt->bindValue(":userId", $userId);
$stmt->bindValue(":text", $text, PDO::PARAM_STR);
// Execute
$result = $stmt->execute();
?>
My Tables:
CREATE TABLE users(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255)
);
INSERT INTO users (name)
VALUES ("Gregor"),
("Liza"),
("Matt"),
("Bob");
CREATE TABLE posts(
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
text VARCHAR(255)
);
You need a loop:
require_once("db.php");
$text = $jsonData["text"];
// Try to fetch the user from the database
$query = "INSERT INTO posts (user_id, text) VALUES (:usersId, :text)";
$stmt = $db->prepare($query);
// Bind value
$stmt->bindParam(":userId", $userId);
$stmt->bindValue(":text", $text, PDO::PARAM_STR);
// Execute
foreach ($jsonData["usersId"] as $userId) {
$result = $stmt->execute();
}
Use bindParam() so it binds to a reference to the variable. That allows you to reassign the variable each time through the loop without re-binding.

SQL IF EXIST UPDATE ELSE INSERT prepared statement

I'm breaking my brains over this, i would realy appriciate help!
This is the code i have so far..
$conn = db_connect();
$sql = "INSERT INTO measurements
(`date`, `weight`, `waist`, `id`) VALUES (?,?,?,?)";
$stmt = $conn-> prepare($sql);
$stmt ->bind_param("sddi", $date, $_POST['weight'], $_POST['waist'], $user_id);
$stmt->execute();
$stmt->close();
$conn->close();
Its a prepared statement for an sql insert. Now i want to change it to a IF EXIST THEN UPDATE ELSE insert the way i am doing right now. something like this but then with a prepared statement:
IF EXISTS
(SELECT * FROM measurements WHERE user_id=’4’)
UPDATE measurements SET (`weight`=40, `waist`=45) WHERE user_id=’4’
ELSE
INSERT INTO measurements
VALUES (`date`='week 1', `weight`= 40, `waist`=45, `id`=4)
I found some articles on stackoverflow about the if EXIST then update else insert but i did not find it with a prepared statement in it that worked for me.
Thanks a thousand!
UPDATE:
i've changed it to dublicate key style.
$sql = "
INSERT INTO measurements (uniqueID, date, weight, waist)
VALUES ('$uniqueID', '$date', '$weight', '$waist')
ON DUPLICATE KEY UPDATE weight= '$weight', waist= '$waist'";
$conn->query($sql);
Now the second part of the question, how do i make this a prepared statement?
To implement Mr. Jones' solution as a mysqli prepared statement, you would code it thus:
$sql = "INSERT INTO measurements
(`uniqueID`, `date`, weight, waist)
VALUES
(?, ?, ?, ?)
ON DUPLICATE KEY
UPDATE weight = ?, waist = ?";
$stmt = $conn->prepare($sql);
$stmt ->bind_param("isdddd", $user_id, $date, $_POST['weight'], $_POST['waist'], $_POST['weight'], $_POST['waist']);
$stmt->execute();
A slightly cleaner implementation would be to use PDO:
$sql = "INSERT INTO measurements
(`uniqueID`, `date`, weight, waist)
VALUES
(:uniqueId, :date, :weight, :waist)
ON DUPLICATE KEY
UPDATE weight = :weight, waist = :waist";
/* $conn is a PDO object */
$stmt = $conn->prepare($sql);
$stmt->execute(array(':uniqueId' => $user_id, ':date' => $date, ':weight' => $_POST['weight'], ':waist' => $_POST['waist']));
Note that with named placeholders, you can use the same name in more than one place and only need to assign the value once.
MySQL's approach to this is INSERT ... ON DUPLICATE KEY UPDATE .... It works well; in particular it avoids race conditions if more than one database connection tries to hit the same row.
This requires the table that's the target of your UPSERT to have a meaningful unique index or primary key. It looks like your id is that key.
You can absolutely use parameter binding to present data to this.
You can read about it here. http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html

PDO not updating

My Script needs to update the table if the name is already in there. But it is not doing it.
This is my code that i'm using
$sth = $db->prepare(
'INSERT INTO track (rsname, overallranknow, overalllevelnow, overallxpnow)
VALUES (:name, :Overalln, :Overall1, :Overall2)
ON DUPLICATE KEY UPDATE
rsname = values(rsname),
overallranknow = values(overallranknow),
overalllevelnow = values(overalllevelnow),
overallxpnow = values(overallxpnow)'
);
$sth->bindValue(':name', $name, PDO::PARAM_STR);
$sth->bindValue(':Overalln', $Overalln, PDO::PARAM_INT);
$sth->bindValue(':Overall1', $Overall[1], PDO::PARAM_INT);
$sth->bindValue(':Overall2', $Overall[2], PDO::PARAM_INT);
$sth->execute();
It should only update when the name is already there. I dont know mutch about PDO so thats why i'm asking so mutch about it.
~Kev (bad english = sorry)
Someone who taught you this query, didn't tell you that you need an unique key for it to work.

using an ID before it is created sql?

Hi i have a question about mysql
this is my query:
$geboortedatum = $_POST['dag'].'-'.$_POST['maand'].'-'.$_POST['jaar'];
$geboortedatum1 = $_POST['dag1'].'-'.$_POST['maand1'].'-'.$_POST['jaar1'];
try{
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "
INSERT INTO clienten
(client_voornaam,
client_achternaam,
client_geboortedatum,
client_geslacht,
client_adres,
client_postcode,
client_woonplaats,
client_contactpersoon,
client_diagnose,
datun_aanmaak)
VALUES (:voornaam,
:achternaam.
:geboortedatum,
:geslacht,
:adres,
:postcode,
:woonplaats,
:contactpersoon,
:diagnose,
:datumaanmaak)
INSERT INTO verzorgers(
wat_is_verzorger,
voornaam_verzorger,
achternaam_verzorger,
geboortedatum_verzorger,
email_verzorger,
geslacht_verzorger,
adres_verzorger,
postcode_verzorger,
woontplaats_verzorger,
tel1_verzorger,
tel2_verzorger,
datum_aanmaak
)
VALUES(
:watisverz
:voornaamverz,
:achternaamverz,
:geboortedatumverz,
:emailverz,
:geslachtverz:
:adresverz,
:postcodeverz,
:woonplaatsverz,
:tel1verz,
:tel2verz,
:datumaanmaak
)
";
$stmt = $db->prepare($sql);
$stmt->bindParam(':voornaam', $_POST['voornaam'], PDO::PARAM_STR);
$stmt->bindParam(':achternaam', $_POST['achternaam'], PDO::PARAM_STR);
$stmt->bindParam(':geboortedatum', $geboortedatum, PDO::PARAM_INT);
$stmt->bindParam(':geslacht', $_POST['geslacht'], PDO::PARAM_STR);
$stmt->bindParam(':adres', $_POST['adres'], PDO::PARAM_STR);
$stmt->bindParam(':postcode', $_POST['postcode'], PDO::PARAM_INT);
$stmt->bindParam(':woonplaats', $_POST['woonplaats'], PDO::PARAM_STR);
$stmt->bindParam(':contactpersoon', $_POST['contactpersoon'], PDO::PARAM_STR);
$stmt->bindParam(':diagnose', $_POST['diagnose'], PDO::PARAM_INT);
$stmt->bindParam(':datumaanmaak', $_POST['datum_toetreding'], PDO::PARAM_INT);
$stmt->execute();
}
catch(PDOException $e)
{
echo '<pre>';
echo 'Regel: '.$e->getLine().'<br>';
echo 'Bestand: '.$e->getFile().'<br>';
echo 'Foutmelding: '.$e->getMessage();
echo '</pre>';
} ?>
Now when the client and verzorger are created, they both get an unique ID.
Client had a column that stores the verzorger ID associated and vica versa.
Is it possible to make a query that also directly stores the IDs created?
EDIT:
ok maybe to difficult, maybe an answer to get only the client id in verzorger?:)
How about something like this (TableAID and TableBID are both AUTO_INCREMENT columns):
INSERT INTO TableA
(SomeColumn)
VALUES
('Blah');
SET #TableAID = LAST_INSERT_ID();
INSERT INTO TableB
(TableAID, AnotherColumn)
VALUES
(#TableAID,'Foobar');
SET #TableBID = LAST_INSERT_ID();
UPDATE TableA
SET TableBID = #TableBID
WHERE TableAID = #TableAID;
What you can do is create a function that queries against a sequence generator:
create function foo returns int
Begin
Declare nextValue int
Set nextValue = SELECT NEXT VALUE FOR mySequence;
End
return (nextValue);
Which would allow you to pass around the value returned from this sequence and you could know "ahead" of time what the values were going to be.
EDIT
Please understand how concurrency works and use either pessimistic or optimistic locking. While this should be obvious, some feel as though it is necessary to include.

Insert on first request; update on all subsequent requests

I'm inserting a row into a MySQL database table. On the first insertion I want a new row to be added, but after that I just want that row to be updated. Here's how I'm doing it. An Ajax request calls the following php file:
<?php
include "base.php";
$bookID = $_POST['bookID'];
$shelfID = $_POST['shelfID'];
$userID = $_SESSION['user_id'];
$query = mysql_query("SELECT shelfID FROM shelves WHERE userID = '$userID' AND shelfID = '$shelfID' AND bookID = '$bookID'");
if (mysql_num_rows($query) == 0) {
$insert = "INSERT INTO shelves (bookID,shelfID,userID) VALUES ('$bookID','$shelfID','$userID')";
mysql_query($insert) or die(mysql_error());
} elseif (mysql_num_rows($query) == 1) { //ie row already exists
$update = "UPDATE shelves SET shelfID = '$shelfID' WHERE userID = '$userID' AND bookID = '$bookID'";
mysql_query($update) or die(mysql_error());
}
?>
As it stands it adds a new row every time.
You should consider using PDO for data access. There is a discussion on what you need to do here: PDO Insert on Duplicate Key Update
I'd flag this as duplicate, but that question is specifically discussing PDO.
You can use the INSERT ... ON DUPLICATE KEY UPDATE syntax. As long as you have a unique index on the data set (i.e. userid + shelfid + bookid) you are inserting, it will do an update instead.
http://dev.mysql.com/doc/refman/5.5/en/insert-on-duplicate.html