I'm working in MySQL and PHP. How in MySQL do you do an update to an existing table with additional records, without causing duplicates? The records are customers and the key to each record is the customer account number such as "1234".
I need to do basically three things. Add a new customer record if their customer account doesn't exist, update an existing customer's information such as their phone number or e-mail address has changed, or delete the customer record if they are no longer a customer.
The data comes in as a feed daily, and marketing wants to have their own data base of customers with their own fields added, which is why I don't simply re-create the database from the daily feed. The goal is to keep it up to date with the feed. Thanks!
I would suggest that you do all of the processing using PHP.
When you're looping through your customer records, first check if the record exists:
$query = "SELECT customer_account_number FROM table WHERE customer_account_number = 1234";
$result= $mysql -> query($query);
$num = $result -> num_rows;
if($num == 1){
// The record already exists so you update.
$update_query = "UPDATE table SET some_field = 'Some Value' WHERE customer_account_number = 1234";
$update_result= $mysqli -> query($update_query);
}else{
// The record doesn't exist so create a record.
$insert_query = "INSERT INTO table (customer_account_number) VALUES (1234)";
$insert_result= $mysqli -> query($insert_query);
}
Alternatively, you could do one select query to get the customer account numbers and pop them in an Array.
$query = "SELECT customer_account_number FROM table";
$result= $mysql -> query($query);
$num = $result -> num_rows;
while($row = $result -> fetch_array(MYSQLI_ASSOC)){
$cust_records[] = $row['customer_account_number'];
}
// Then for each customer record your looping through.
if(in_array($customer_record_id, $cust_records)){
// It exists - so just update
$update_query = "UPDATE table SET some_field = 'Some Value' WHERE customer_account_number = 1234";
$update_result= $mysqli -> query($update_query);
}else{
// Doesn't exist - insert
$insert_query = "INSERT INTO table (customer_account_number) VALUES (1234)";
$insert_result= $mysqli -> query($insert_query);
}
Also, as suggested by Tadman, if you make sure the customer account number is a UNIQUE column then that will add extra protection to make sure duplicates can't be added.
use the insert on duplicate update query. It does the inserting and updating part of your question. For deletion you could have another query i suppose.
refer: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
MySQL supports "on duplicate key update" syntax.
This basically allows you to specify which fields to update if the Primary Key (your account number) already exists, all in one SQL statement.
mysql syntax
You'd have to handle deletion with a separate SQL statement, since this logic would almost certainly come from your customer feed.
Run a SELECT id FROM table before inserting and save the ids as keys in an associative array. By simply going if a key is set, you can check if the id exists in the database and do processing based on that.
If you remove each customer process, in the end you'll be left with an array of customers no longer existing.
Executing this one time saves some SQL load.
$result = mysql_query('SELECT id FROM table');
$ids = array();
while($row = mysql_fetch_assoc($result)){
$ids[$row['id']] = true;
}
foreach($objects as $key => $obj) {
if($ids[$obj['id']]) {
// exists, update!
unset($ids[$obj['id']]);
} else {
// new, insert!
}
}
// go through the remaining people not added
for($ids as $deleted_id) {
// delete this guy
}
Related
On my XAMPP server I have a database table on phpMyAdmin. In that table, I have a few columns, and one of them is id column (Integer).
I want to get the latest added item's ID, increment it by one and then assign it to a new item that the function adds to the table.
The problem is that whenever there is a new item, it is automatically assigned with 1 as id, nothing above 1.
$sql = "SELECT * FROM items";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// output data of each row
while ($row = $result->fetch_assoc()) {
if( $_SESSION["increment"] == "yes"){
$_SESSION["id"] = $row["id"];
}else
$_SESSION["id"]=$_SESSION["id"]+1;
}
} else {
$_SESSION["id"] = 1;
}
This will give you last increment Id.
$sql = "SELECT id FROM items order by id DESC LIMIT 0,1";
Then you dont want have a while loop to find last increment Id.
error reporting said what? and mysqli_error($conn)?
-- Fred-ii-
The above request by Fred -ii- sums it up, if your ->num_rows is returning zero or not a number (false) then you have an SQL error, so you need to check your error logs, and check your database connection.
Have you started your session with session_start?
Do you intend that the first else calls without brackets, only executing the single following line, $_SESSION["id"]=$_SESSION["id"]+1; ?
It seems to me that you need well known AUTO_INCREMENT functionality built inside MySQL database. Just define in your database schema for your table that column is AUTO_INCREMENT column type, and it will be automatically incremented by 1 upon each new insert into table.
I need to synchronize specific information between two databases (one mysql, the other a remote hosted SQL Server database) for thousands of rows. When I execute this php file it gets stuck/timeouts after several minutes I guess, so I wonder how I can fix this issue and maybe also optimize the way of "synchronizing" it.
What the code needs to do:
Basically I want to get for every row (= one account) in my database which gets updated - two specific pieces of information (= 2 SELECT queries) from another SQL Server database. Therefore I use a foreach loop which creates 2 SQL queries for each row and afterwards I update those information into 2 columns of this row. We talk about ~10k Rows which needs to run thru this foreach loop.
My idea which may help?
I have heard about things like PDO Transactions which should collect all those queries and sending them afterwards in a package of all SELECT queries, but I have no idea whether I use them correctly or whether they even help in such cases.
This is my current code, which is timing out after few minutes:
// DBH => MSSQL DB | DB => MySQL DB
$dbh->beginTransaction();
// Get all referral IDs which needs to be updated:
$listAccounts = "SELECT * FROM Gifting WHERE refsCompleted <= 100 ORDER BY idGifting ASC";
$ps_listAccounts = $db->prepare($listAccounts);
$ps_listAccounts->execute();
foreach($ps_listAccounts as $row) {
$refid=$row['refId'];
// Refsinserted
$refsInserted = "SELECT count(username) as done FROM accounts WHERE referral='$refid'";
$ps_refsInserted = $dbh->prepare($refsInserted);
$ps_refsInserted->execute();
$row = $ps_refsInserted->fetch();
$refsInserted = $row['done'];
// Refscompleted
$refsCompleted = "SELECT count(username) as done FROM accounts WHERE referral='$refid' AND finished=1";
$ps_refsCompleted = $dbh->prepare($refsCompleted);
$ps_refsCompleted->execute();
$row2 = $ps_refsCompleted->fetch();
$refsCompleted = $row2['done'];
// Update fields for local order db
$updateGifting = "UPDATE Gifting SET refsInserted = :refsInserted, refsCompleted = :refsCompleted WHERE refId = :refId";
$ps_updateGifting = $db->prepare($updateGifting);
$ps_updateGifting->bindParam(':refsInserted', $refsInserted);
$ps_updateGifting->bindParam(':refsCompleted', $refsCompleted);
$ps_updateGifting->bindParam(':refId', $refid);
$ps_updateGifting->execute();
echo "$refid: $refsInserted Refs inserted / $refsCompleted Refs completed<br>";
}
$dbh->commit();
You can do all of that in one query with a correlated sub-query:
UPDATE Gifting
SET
refsInserted=(SELECT COUNT(USERNAME)
FROM accounts
WHERE referral=Gifting.refId),
refsCompleted=(SELECT COUNT(USERNAME)
FROM accounts
WHERE referral=Gifting.refId
AND finished=1)
A correlated sub-query is essentially using a sub-query (query within a query) that references the parent query. So notice that in each of the sub-queries I am referencing the Gifting.refId column in the where clause of each sub-query. While this isn't the best for performance because each of those sub-queries still has to run independent of the other queries, it would perform much better (and likely as good as you are going to get) than what you have there.
Edit:
And just for reference. I don't know if a transaction will help here at all. Typically they are used when you have several queries that depend on each other and to give you a way to rollback if one fails. For example, banking transactions. You don't want the balance to deduct some amount until a purchase has been inserted. And if the purchase fails inserting for some reason, you want to rollback the change to the balance. So when inserting a purchase, you start a transaction, run the update balance query and the insert purchase query and only if both go in correctly and have been validated do you commit to save.
Edit2:
If I were doing this, without doing an export/import this is what I would do. This makes a few assumptions though. First is that you are using a mssql 2008 or newer and second is that the referral id is always a number. I'm also using a temp table that I insert numbers into because you can insert multiple rows easily with a single query and then run a single update query to update the gifting table. This temp table follows the structure CREATE TABLE tempTable (refId int, done int, total int).
//get list of referral accounts
//if you are using one column, only query for one column
$listAccounts = "SELECT DISTINCT refId FROM Gifting WHERE refsCompleted <= 100 ORDER BY idGifting ASC";
$ps_listAccounts = $db->prepare($listAccounts);
$ps_listAccounts->execute();
//loop over and get list of refIds from above.
$refIds = array();
foreach($ps_listAccounts as $row){
$refIds[] = $row['refId'];
}
if(count($refIds) > 0){
//implode into string for use in query below
$refIds = implode(',',$refIds);
//select out total count
$totalCount = "SELECT referral, COUNT(username) AS cnt FROM accounts WHERE referral IN ($refIds) GROUP BY referral";
$ps_totalCounts = $dbh->prepare($totalCount);
$ps_totalCounts->execute();
//add to array of counts
$counts = array();
//loop over total counts
foreach($ps_totalCounts as $row){
//if referral id not found, add it
if(!isset($counts[$row['referral']])){
$counts[$row['referral']] = array('total'=>0,'done'=>0);
}
//add to count
$counts[$row['referral']]['total'] += $row['cnt'];
}
$doneCount = "SELECT referral, COUNT(username) AS cnt FROM accounts WHERE finished=1 AND referral IN ($refIds) GROUP BY referral";
$ps_doneCounts = $dbh->prepare($doneCount);
$ps_doneCounts->execute();
//loop over total counts
foreach($ps_totalCounts as $row){
//if referral id not found, add it
if(!isset($counts[$row['referral']])){
$counts[$row['referral']] = array('total'=>0,'done'=>0);
}
//add to count
$counts[$row['referral']]['done'] += $row['cnt'];
}
//now loop over counts and generate insert queries to a temp table.
//I suggest using a temp table because you can insert multiple rows
//in one query and then the update is one query.
$sqlInsertList = array();
foreach($count as $refId=>$count){
$sqlInsertList[] = "({$refId}, {$count['done']}, {$count['total']})";
}
//clear out the temp table first so we are only inserting new rows
$truncSql = "TRUNCATE TABLE tempTable";
$ps_trunc = $db->prepare($truncSql);
$ps_trunc->execute();
//make insert sql with multiple insert rows
$insertSql = "INSERT INTO tempTable (refId, done, total) VALUES ".implode(',',$sqlInsertList);
//prepare sql for insert into mssql
$ps_insert = $db->prepare($insertSql);
$ps_insert->execute();
//sql to update existing rows
$updateSql = "UPDATE Gifting
SET refsInserted=(SELECT total FROM tempTable WHERE refId=Gifting.refId),
refsCompleted=(SELECT done FROM tempTable WHERE refId=Gifting.refId)
WHERE refId IN (SELECT refId FROM tempTable)
AND refsCompleted <= 100";
$ps_update = $db->prepare($updateSql);
$ps_update->execute();
} else {
echo "There were no reference ids found from \$dbh";
}
I have a database listed as $db under mysqli. This database is contains into two tables, I listed them below as table and table2 (just for this example). Table2's rows requires an id from table. This is fine, but there might be a problem adding the columns into table2 thus requiring a rollback routine. However, it doesn't seem to be working.
I started with turning off the auto-commit. I then tried to put in the rollback command even though I am using the die command to signal a failure. As far as I am concerned the transaction could be blasted into oblivion in mid operation and the database should still be stable. So I am not sure what is going on here unless the database is completely ignoring the fact that I am trying to turn off auto-commit.
The basic structure of my code is listed below:
function problem($str)
{
global $db;
mysqli_rollback($db);
die($str);
}
mysqli_autocommit($db,false);
//Basic check if exists
$sqlstr = "SELECT * FROM table WHERE name = '$name';";
$r = mysqli_query($db,$sqlstr);
if (mysqli_num_rows($r)>0){problem("A row already exists under that id");}
//Insert the row
$sqlstr = "INSERT INTO table (name,v1,v2,v3) VALUES ('$name','$v1','$v2','$v3');";
$r = mysqli_query($db,$sqlstr);
if (!$r){problem("Could not insert into the table. $sqlstr");}
//Get the generated id part 1
$sqlstr = "SELECT id FROM table WHERE name = '$name';";
$r = mysqli_query($db,$sqlstr);
if (!$r){problem("Could not add into the table. $sqlstr");}
//Get the generated id part 2
$row = mysqli_fetch_assoc($r);
$eid = $row['id'];
//A simple loop
$count = count($questions);
for ($i=1;i<=$count;$i++)
{
//This is where it typically could die.
$r = mysqli_query($db,"INSERT INTO table2 VALUES (...);");
if (!$r){problem("Could not add to the table2. $sqlstr");}
}
mysqli_commit($db);
Is there something I am missing? I tried to follow the examples I found for the auto-commit as closely as I could.
Transactions only work if the table engine supports them, e.g. InnoDB.
I'm not sure how to accomplish this issue which has been confusing me for a few days. I have a form that updates a user record in MySQL when a checkbox is checked. Now, this is how my form does this:
if (isset($_POST['Update'])) {
$paymentr = $_POST['paymentr']; //put checkboxes array into variable
$paymentr2 = implode(', ', $paymentr); //implode array for mysql
$query = "UPDATE transactions SET paymentreceived=NULL";
$result = mysql_query($query);
$query = "UPDATE transactions SET paymentdate='0000-00-00'";
$result = mysql_query($query);
$query = "UPDATE transactions SET paymentreceived='Yes' WHERE id IN ($paymentr2)";
$result = mysql_query($query);
$query = "UPDATE transactions SET paymentdate=NOW() WHERE id IN ($paymentr2)";
$result = mysql_query($query);
foreach ($paymentr as $v) { //should collect last updated records and put them into variable for emailing.
$query = "SELECT id, refid, affid FROM transactions WHERE id = '$v'";
$result = mysql_query($query) or die("Query Failed: ".mysql_errno()." - ".mysql_error()."<BR>\n$query<BR>\n");
$trans = mysql_fetch_array($result, MYSQL_ASSOC);
$transactions .= '<br>User ID:'.$trans['id'].' -- '.$trans['refid'].' -- '.$trans['affid'].'<br>';
}
}
Unfortunately, it then updates ALL the user records with the latest date which is not what I want it to do. The alternative I thought of was, via Javascript, giving the checkbox a value that would be dynamically updated when the user selected it. Then, only THOSE checkboxes would be put into the array. Is this possible? Is there a better solution? I'm not even sure I could wrap my brain around how to do that WITH Javascript. Does the answer perhaps lie in how my mysql code is written?
--
Edit: Ok, just more information. The SQL Queries I have going on - the first two are to wipe everything clean (in case a checkbox is UNCHECKED) and then next they are updating the SQL queries based on which checkboxes are checked upon post.
However, I'm thinking this is a bad way to do it. Why force the database to first wipe out ALL data for paymetreceived, paymetdate? The problem with this, also, is that *all the subsequent checkboxes, regardless of how long ago they were checked, get updated in the SQL query as it is now.*There's got to be a way to update it better. I'm just not sure HOW to do it. any ideas?
You are not filtering by id in this queries:
$query = "UPDATE transactions SET paymentreceived=NULL";
$query = "UPDATE transactions SET paymentdate='0000-00-00'";
Try adding: WHERE id IN ($paymentr2)";
The problem is in your first 2 sql UPDATE statements. You don't provide a WHERE clause, so that's going to update all your records. You could add:
WHERE id IN ($paymentr2)
to your first two UPDATE statements
Hey, I have a field called STATUS and it is either 1 to show or 0 to hide. My code is below. I am using an edit in place editor with jQuery. Everytime you update it creates a new ROW which I want, but I want only the new one to have STATUS = 1 and the others to 0. Any ideas on how I would do that?
<?php
include "../../inc/config.inc.php";
$temp = explode("_", $_REQUEST['element_id'] );
$field = $temp[0];
$id = $temp[1];
$textboxval = stripslashes(mysql_real_escape_string(preg_replace('/[\$]/',"",$_REQUEST["update_value"])));
$query = "INSERT INTO notes ($field,status,date,c_id) VALUES ('$textboxval','1',NOW(),'$id')";
mysql_query($query);
echo($_REQUEST['update_value']);
?>
I am not sure exactly what you mean - do you want to make all the entries except the new one have status = 0? If so, just issue an update before the insert:
UPDATE notes SET status = 0
However, I should also note that you have a potential SQL injection to worry about. By stripping slashes after applying "mysql real escape string", you are potentially allowing someone to put text in your SQL statement that will execute an arbitrary SQL statement.
Something like this, sorry for the post before, I mis read it the first time then went back:
<?php
include "../../inc/config.inc.php";
$temp = explode("_", $_REQUEST['element_id'] );
$field = $temp[0];
$id = $temp[1];
$textboxval = mysql_real_escape_stringstripslashes((preg_replace('/[\$]/',"",$_REQUEST["update_value"])));
// set older entries to 0 - to not show but show in history
$hide_notes = "UPDATE notes SET status = 0";
mysql_query($hide_notes);
// add new entry with status of 1 to show only latest note
$query = "INSERT INTO notes ($field,status,date,c_id) VALUES ('$textboxval','1',NOW(),'$id')";
mysql_query($query);
echo($_REQUEST['update_value']);
?>
i just ran in to a problem I didn't of the set up of my table doesn't allow me to show more than one client a time and i will be having numerous clients, my bad on planning ha
You really want to get the ID of the newly generated row and then trigger an UPDATE where you all rows where the ID is not the new row, e.g.
UPDATE notes SET status = 0 WHERE id != $newly_generated_id
If the ID column in your table is using AUTO_INCREMENT you can get its ID via "SELECT LAST_INSERT_ID()" and then use the return value in that statement in your UPDATE statement.
Pseudo code:
$insert = mysql_query("INSERT INTO ...");
$last_id = mysql_query("SELECT LAST_INSERT_ID()");
$update = mysql_quqery("UPDATE notes SET status = 0 WHERE id != $last_id");
The only caveat to this approach is where you might have a brief moment in time where 2 rows have status=1 (the time between your INSERT and the UPDATE). I would wrap all of this in a transaction to make the whole unit more atomic.