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);
Related
I'm trying to insert data into my database. I'm able to insert it using INSERT INTO. However I want to check, if the database already has the ID and if it has I want it to just update the records.
$check = $db->query("SELECT Fuid FROM users WHERE Fuid='$fuid_db'");
//IF the user is new
if(empty($check))
{
$insert = $db->query("INSERT INTO users (Fuid, Ffname, Femail) VALUES ('$fuid_db', '$ffname_db', '$femail_db'");
}
else
{
//ELSE update the information
$update = $db->query("UPDATE users SET Ffname='$ffname_db', Femail='$femail_db' WHERE Fuid='$fuid_db'");
}
How come this is not working? I'm using ERROR_REPORTING(E_ALL),
but I'm not getting any errors. It seems like the issue is the "checking" before inserting. Without the checking part it works fine but now it's not inserting anything.
The Problem
$db->query() returns an (maybe empty) mysqli_result object. So empty($check) evaluates to false (since empty($var) returns true only if $var is zero, false, an empty string, an empty array or null) and thus an UPDATE is performed, no matter if there is a corresponding id or not.
To check if there is a record you have to use $check->num_rows to get the number of records containing the id.
Better
The ON DUPLICATE KEY UPDATE command should work for you.
INSERT INTO users (Fuid, Ffname, Femail)
VALUES ('$fuid_db', '$ffname_db', '$femail_db')
ON DUPLICATE KEY UPDATE users
SET Ffname = '$ffname_db', Femail = '$femail_db'
This way you don't have to make a SELECT first, so you reduce the number of database calls.
Notice: This only works, if Fuid is set as a unique key (or primary key).
Security
The way you use the SQL commands is vulnerable to SQL injection. You should use prepared statements.
Using prepared statements, your code would look like this:
$stmt = $db->prepare("INSERT INTO users (Fuid, Ffname, Femail)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE users
SET Ffname = ?, Femail = ?");
$stmt->bind_param('issss',$fuid_db,$ffname_db, $femail_db,$ffname_db, $femail_db)
$stmt->execute();
First and foremost, for security reasons you should not insert value that way.
But if we ignore it for now, you can instead use ON DUPLICATE KEY UPDATE as below provided that Fuid is a primary key
INSERT INTO users (Fuid, Ffname, Femail) VALUES ('$fuid_db', '$ffname_db', '$femail_db')
ON DUPLICATE KEY UPDATE users SET Ffname = '$ffname_db', Femail = '$femail_db'
I am using try catch algorithm when inserting a record to a MySQL table. My scripting language is PHP.
try {
/*
UDID generation algo goes here.
*/
$sql = "INSERT INTO tablex (udid, name)
VALUES ('$udid', 'Doe')";
$conn->exec($sql);
echo "New record created";
}
catch(PDOException $e)
{
echo "Error"
}
$conn = null;
How do I re-write the above so that instead of try catch, I use a loop. If there is an error, try again. Break out of the loop if there is no error.
There is "While True" but I don't know if the "Try Catch" should be part of the While True loop..
The purpose of this is to save a UDID - the unique value is set in MYSQL. If I generate the same value, I may get the error. Hence, why the loop.
I feel like you're going about this the wrong way. Instead of trying to insert a value that might not be unique, I would try to see if that value exists first. You could perform a query such as
do {
//$udid = create uuid
$query = "SELECT COUNT(*) FROM tablex WHERE udid = :udid";
$statement = $pdo->prepare($query);
$statement->bindValue("udid", $udid);
$result = $statement->fetchAll();
} while ($result["count"] === 1);
//insert row into table
This is just off the top of my head and it can be refactored even better. You could also leverage your database to generate the UDID for you if you prefer. You could run a query to get all of the udid's from your table and just run in_array to check to see if the value is there, then you're only hitting your database twice. Anyway, you really don't want to try to abuse a try catch like that.
mysql has a function for something like this you would add an "ON DUPLICATE KEY UPDATE" to your insert statement
Example:
INSERT INTO table(x, y, z) values (?, ?, ?) ON DUPLICATE KEY UPDATE x=?;
Also see this link for more info:
http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
I have the below code, which works perfect. What i want to do is to check the refNo first to see if there are duplicates entries in MySQL. If there is then appear a warning message, otherwise appear a "ok" message. How can i do that with PDO? Any help?
(include("db.php"));
$SQLquery = "INSERT INTO mydatabase (refNo, name)
VALUES ('".$_POST["refNo"]."', '".$_POST["name"]."');";
$STH = $dbc->query($SQLquery);
?>
edit: Hello guys,
i prefer not to add primary keys. Is there any other way?
Set up refNo as a primary key. You could also create it as unique but that defeats the purpose - your reference number appears to be a unique primary identifier. Perfect choice for a primary key.
Further, change your query
try {
$SQLquery = "INSERT INTO mydatabase (refNo, name) VALUES (:refNo, :name)";
$SQLquery = $dbc->prepare($SQLquery);
$SQLquery->bindValue(':refNo', $_POST['refNo']);
$SQLquery->bindValue(':name', $_POST['name']);
$SQLquery->execute();
} catch (Exception $e) {
die("Insert error");
}
$count = $SQLquery->rowCount();
if ($count == 1) {
echo "Record added!";
}
This binds the post value to prevent SQL injection too.
Edit: You could follow this up with $count = $SQLquery->rowCount(); which will be 1 if the insert was successful, as it appears you've edited your question since you posted it for more info.
If you want to do this without using a database level constraint, you'll need to do an extra SELECT statement before inserting into the table. But that gives you no absolute guarantees, as it might be two processes want to insert the same row at the same time and they will still succeed.
-- it'll look a little something like this; I'm not familiar with PDO but the structure should be the same
$selectQuery = "SELECT * FROM mydatabase
WHERE refno = '".$_POST["refNo"]."'";
$res = $dbc->query( $selectQuery );
if( $res->count() > 0 ) {
// this result already exists; show error
}
else {
// this result is new; put the insert query here
}
I've been stuck on this for a few hours now ...
Here's my code:
$SQLQuery1 = $db_info->prepare("SELECT COUNT(ID) FROM menusize WHERE typesize=:typesize");
$SQLQuery1->bindValue(':typesize',$_POST['typesize'],PDO::PARAM_STR);
$SQLQuery1->execute();
if($SQLQuery1->fetchColumn() > 0) {
$SQLQuery2 = $db_info->prepare("INSERT INTO menucatagorysize (menucatagory_ID,menusize_ID) VALUES (:catagoryid,(SELECT ID FROM menusize WHERE typesize=:typesize))");
$SQLQuery2->bindValue(':typesize',$_POST['typesize'],PDO::PARAM_STR);
$SQLQuery2->bindValue(':catagoryid',$_POST['catagoryid'],PDO::PARAM_STR);
$SQLQuery2->execute();
} else {
$SQLQuery2 = $db_info->prepare("INSERT INTO menusize (typesize) VALUES (:typesize);
SET #menusizeid=LAST_INSERT_ID();
INSERT INTO menucatagorysize (menusize_ID,menucatagory_ID) VALUES (#menusizeid,:catagoryid)");
$SQLQuery2->bindValue(':typesize',$_POST['typesize'],PDO::PARAM_STR);
$SQLQuery2->bindValue(':catagoryid',$_POST['catagoryid'],PDO::PARAM_STR);
$SQLQuery2->execute();
}
$SQLQuery3 = $db_info->prepare("SELECT DISTINCT(menuitem_ID) FROM menuprice WHERE menucatagory_ID=:catagoryid");
$SQLQuery3->bindValue(':catagoryid',$_POST['catagoryid'],PDO::PARAM_STR);
$SQLQuery3->execute();
$rows = $SQLQuery3->fetchAll(PDO::FETCH_ASSOC);
So, it will run through the if statement fine, running $SQLQuery1 and $SQLQuery2 (Which ever one is required) without any problems, errors or warnings. But, if it runs the else { part of the code, it will not run $SQLQuery3. Any thoughts?
Thanks :D
EDIT: Got it to work by doing $SQLQuery2=NULL in the else statement ... Sucks that I still cant figure out why it wouldnt work the original way.
It appears that you're trying to enforce a uniqueness constraint over the typesize column of your menusize table from within your application code. However, the database can do this for you—which will make your subsequent operations much simpler:
ALTER TABLE menusize ADD UNIQUE (typesize)
Now, one can simply attempt to insert the posted value into the table and the database will prevent duplicates arising. Furthermore, as documented under INSERT ... ON DUPLICATE KEY UPDATE Syntax:
If a table contains an AUTO_INCREMENT column and INSERT ... ON DUPLICATE KEY UPDATE inserts or updates a row, the LAST_INSERT_ID() function returns the AUTO_INCREMENT value. Exception: For updates, LAST_INSERT_ID() is not meaningful prior to MySQL 5.1.12. However, you can work around this by using LAST_INSERT_ID(expr). Suppose that id is the AUTO_INCREMENT column. To make LAST_INSERT_ID() meaningful for updates, insert rows as follows:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3;
Therefore, you can do:
$db_info->prepare('
INSERT INTO menusize (typesize) VALUES (:typesize)
ON DUPLICATE KEY UPDATE typesize=LAST_INSERT_ID(typesize)
')->execute(array(
':typesize' => $_POST['typesize']
));
$db_info->prepare('
INSERT INTO menucatagorysize
(menusize_ID, menucatagory_ID)
VALUES
(LAST_INSERT_ID(), :catagoryid)
')->execute(array(
':catagoryid' => $_POST['catagoryid']
));
$stmt = $db_info->prepare('
SELECT DISTINCT menuitem_ID
FROM menuprice
WHERE menucatagory_ID = :catagoryid
');
$stmt->execute(array(
':catagoryid' => $_POST['catagoryid']
));
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// etc.
}
(As an aside, the English word is spelled cat*e*gory, not cat*a*gory.)
A table in my DB has the following columns: ID, campaign_id, opt_out_day
The method that inserts a set of "opt-out-days" - an array of DATETIME values - is as follows:
/**
* #param Integer $campaignId
* #param Array $optOutDays Array of DATETIME values
*/
public function addCampaignOptOutDays($campaignId, $optOutDays) {
$query = "INSERT INTO `".self::$tblOptOutDays."`
(campaign_id, opt_out_day)
VALUES
(:campaign_id, :opt_out_day)";
try {
$this->connection->beginTransaction();
$query = $this->connection->prepare($query);
$query->bindParam(':campaign_id', $campaignId, \PDO::PARAM_INT);
foreach ($optOutDays as $day) {
$query->bindParam(':opt_out_day', $day, \PDO::PARAM_STR);
$query->execute();
}
$this->connection->commit();
} catch (\PDOException $e) {
$this->connection->rollback();
throw new \Models\Database\DatabaseException($e->getMessage());
}
}
How can I modify the query in order to prevent duplicating the same opt-out-day for a campaign? In other words, multiple rows with the same opt-out-day can exist in this table, as long as they have different a 'campaign-id'.
Adding a unique key for the (campaign_id, opt_out_day) is not an option though, as I don't want to throw exceptions when such situations occur, I just want to not add the pair again.
Try using the INSERT ... ON DUPLICATE KEY UPDATE id=id
http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
This will avoid an extra SELECT query but will also avoid an error if you have duplicate keys.
There's also INSERT IGNORE ... but that might ignore other errors as well
As Jakub Kania commented, you could use a unique index and use INSERT IGNORE:
With IGNORE, the row still is not inserted, but no error occurs.
Ignored errors may generate warnings instead, although duplicate-key
errors do not.
Then, simply check the affected rows value to see if the insert was successful.
So, other errors can still be detected, as warnings. I think INSERT ... ON DUPLICATE KEY UPDATE is a better solution, given you add the unique index because checking warnings can be a pain.
Im not familiair with php, but what if you select from the table and check if that returns a value, if not then insert?
Try
$checkquery = "SELECT 1 FROM `".self::$tblOptOutDays."`
WHERE campaign_id=:campaign_id AND opt_out_day=:opt_out_day)";
Then
IF $checkquery <> 1 THEN
// run insert