I have a simple MYSQL query:
INSERT INTO table (col1,col2) VALUES ('1','2')
ON DUPLICATE KEY UPDATE col1 = '1', col2 = '2'
I use PHP PDO statements to query the database. Is there a way to know if the query executed resulted into a new inserted row or an existing was updated?
One way to do so is to get the number of rows before executing the query, then get the number of rows after executing the query, if they're not equal, it means a new row was inserted and if they are equal, it means a row was updated.
$sql = "SHOW TABLE STATUS LIKE 'TABLE_NAME'";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$row = $stmt->fetch();
$number_of_rows_before = $row['Rows'];
// Do your query here, afterwards
$stmt = $pdo->prepare($sql);
$stmt->execute();
$row = $stmt->fetch();
$number_of_rows_after = $row['Rows'];
// If condition
if($number_of_rows_before == $number_of_rows_after) // Update was executed
else // a new row was inserted.
Just use mysqli_affected_rows,it returns the number of rows affected by the last INSERT, UPDATE, REPLACE or DELETE query.
From PHP documentation:
In the case of "INSERT ... ON DUPLICATE KEY UPDATE" queries, the return value will be 1 if an insert was performed, or 2 for an update of an existing row.
see https://www.php.net/manual/en/function.mysql-affected-rows.php
From Mysql manual:
"With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if
the row is inserted as a new row and 2 if an existing row is updated."
See: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
This is the most reliable way to do it.
maybe you put the answer right into the query like:
INSERT INTO table (col1,col2, col_type) VALUES ('1','2','inserted')
ON DUPLICATE KEY UPDATE col1 = '1', col2 = '2', col_type = 'updated'
Related
I have an xml file where I get the data. I record this data in the database and make updates. Now I am faced with a situation whose logic I cannot solve.
I want to do; I want to check the data in the database for the data I get from XML, to add if there is any unattached data and to update it if it is added.
Sample codes are as follows. Thank you in advance for your support
$current = simplexml_load_file('http://example.com/simple.xml');
foreach($current->simple as $item){
// Database Control data
$tax = $item->tax;
$data = $db->query("SELECT*FROM current WHERE tax_number = '$tax' ");
foreach($data->results() as $row){
if(isset($data))
{
// Edit
}
else
{
// Insert
}
}
}
According to the codes above, there is a situation like this. For example, if there are 50 data in the XML file, it returns 50 * 500 times if there is 500 data in the current table, multiplying each data by the number in the table I want to control. And he just adds.
Considering you'll have unique value per tax_number in your table. You should make this tax_number column as unique.
ALTER TABLE `current`
ADD UNIQUE INDEX `tax_number` (`tax_number`);
Once done you can use mysql INSERT ... ON DUPLICATE KEY UPDATE feature to insert a record or update existing record in single query for a given tax_number.
Considering you have col1, col2 and tax_number as columns in your table and you want to update col1 if this record already exists. So mysql query would be
INSERT INTO `current` (col1, col2, tax_number)
VALUES ('a', 'b', 12345)
ON DUPLICATE KEY UPDATE col1 = 'a';
Note: Your code is vulnerable to sql injection, make sure you update your code by PDO with parameter binding.
So corresponding code in php would be like this.
$current = simplexml_load_file('http://example.com/simple.xml');
foreach($current->simple as $item){
$query = $db->prepare('INSERT INTO current (currentDocumentNumber, currentTaxNumber, currentIdentity,currentOperationType,currentAmountOfDebt,currentAmountDue,currentAccountDate)
VALUES (:doc_number, :tax_number, :identity, :operation_type, :debt_amount, :due_amount, :date)
ON DUPLICATE KEY UPDATE currentAmountOfDebt = :debt_amount, currentAmountDue = :due_amount');
$query->bindParam(':doc_number', $item->EVRAK_NO, PDO::PARAM_STR);
$query->bindParam(':tax_number', $item->VERGI_NO, PDO::PARAM_INT);
$query->bindParam(':identity', $item->TC_KIMLIK_NO, PDO::PARAM_STR);
$query->bindParam(':operation_type', $item->ISLEM_TURU, PDO::PARAM_INT);
$query->bindParam(':debt_amount', $item->KPB_BTUT, PDO::PARAM_STR);
$query->bindParam(':due_amount', $item->KPB_ATUT, PDO::PARAM_STR);
$query->bindParam(':date', $item->TARIHI, PDO::PARAM_STR);
$query->execute();
}
I used INSERT INTO SELECT to copy values (multiple rows) from one table to another. Now, my problem is how do I insert rows with its corresponding IDs from different tables (since it's normalized) into a gerund table because it only outputs one row in my gerund table. What should I do to insert multiple rows and their corresponding IDs in the gerund table.
My code for the gerund table goes like this.
$insert = "INSERT INTO table1 SELECT * FROM sourcetable"; // where id1 is pk of table1.
$result =mysqli_query($conn,$insert)
$id1=mysqli_insert_id($conn);
Now table 1 has inserted multiple rows same as the other 2 tables.
Assuming id.. are the foreign keys
INSERT INTO gerundtable (pk, id1,id2,id3) VALUES ($id1,$id2,$id3);
My problem is it doesn't yield multiple rows.
According to MySql documentation:
For a multiple-row insert, LAST_INSERT_ID() and mysql_insert_id() actually return the AUTO_INCREMENT key from the first of the inserted rows. This enables multiple-row inserts to be reproduced correctly on other servers in a replication setup.
So, grab the number of records being copied, and the LAST_INSERT_ID() and you should be able to map exact IDs with each copied row.
In the lines of:
$mysqli->query("Insert Into dest_table Select * from source_table");
$n = $mysqli->affected_rows; // number of copied rows
$id1 = $mysqli->insert_id; // new ID of the first copied row
$id2 = $mysqli->insert_id + 1; // new ID of the second copied row
$id3 = $mysqli->insert_id + 2; // new ID of the third copied row
...
$mysqli->query("INSERT INTO gerundtable (pk, id1,id2,id3) VALUES ($id1,$id2,$id3)");
Thank you for trying to understand and also answering my question. I resolved my own code. I used while loop to get the ids of every row and didn't use INSERT INTO SELECT.
Here is the run down. SInce I'm just using my phone bare with my way posting.
$sqlselect = SELECT * FROM table1;
While($row=mysqli_fetch_array(table1){
$insertquery...
$id1=mysqli_insert_id($conn)
$insertgerundtable = INSERT INTO gerundtable VALUES ( $id1, $id2);
}
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.)
When using ON DUPLICATE KEY UPDATE in MySQL (with PDO), how can one check if an extant row was updated, or in a new row was inserted? I'm using PHP's PDO with a prepared query to insert:
$sql = "INSERT INTO some_table (f_name, l_name) ";
$sql.= "VALUES (:f_name, :l_name) ";
$sql.= "ON DUPLICATE KEY UPDATE ";
$sql.= " f_name = VALUES(f_name) ";
$sql.= " l_name = VALUES(l_name) ";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':f_name', 'Jeff');
$stmt->bindValue(':l_name', 'Atwood');
$stmt->execute();
Now I would like to output Updated! or Added! as appropriate. How can I check this?
You can check by checking number of affected rows.
Whenever a row is inserted it returns exactly same number as number of inserted rows. However, when it update, It multiply by 2 and return as affected rows.
For example,
If you are adding two row, and
Row is create: affected row would be 2
Row is updated: affected row would be 4
Reference: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
Also, to check number of affected rows you can use rowCount function ( http://in3.php.net/manual/en/pdostatement.rowcount.php )
I'm new to php. So, please forgive me if this seems like a dumb question.
Say i have a MySQL insert statement insert into table (a,b) values (1,2),(3,4),(5,6). table 'table' has a auto increment field called 'id'.
how can I retrieve all the ids created by the insert statement above?
It will be great if i get an example that uses mysqli.
You can't. I would suggest that you maintain your own ids (using guid or your own auto-increment table) and use it when you insert into the table.
But it's possible to get the auto-increment value for the last inserted using LAST_INSERT_ID():
http://dev.mysql.com/doc/refman/5.0/en/getting-unique-id.html
AngeDeLaMort's answer is almost right. Certainly, the most appropriate way to deal with the problem is to insert one row at a time and poll the insert_id or generate the sequence elsewhere (which has additional benefits in terms of scalability).
I'd advise strongly against trying to determine the last insert_id and comparing this the most recent insert_id after the insert - there's just too may ways this will fail.
But...an alternative approach would be:
....
"INSERT INTO destn (id, data, other, trans_ref)
SELECT id, data, other, connection_id() FROM source";
....
"SELECT id FROM destn WHERE trans_ref=connection_id()";
....
"UPDATE destn SET trans_ref=NULL where trans_ref=connection_id()";
The second query will return the ids generated (note that this assumes that you use the same connection for all 3 queries). The third query is necessary because connection ids to go back into the pool when you disconnect (i.e. are reused).
C.
In some cases, if you have another identifier of sort such as a UserID, you could filter your query by UniqueID's greater than or equal to mysql_insert_id(), limit by the number of affected rows and only display those by the user. This would really only work inside of a transaction.
$SQL = "INSERT INTO Table
(UserID, Data)
VALUES
(1,'Foo'),
(1,'Bar'),
(1,'FooBar')";
$Result = mysql_query($SQL);
$LastID = mysql_insert_id();
$RowsAffected = mysql_affected_rows();
$IDSQL = "SELECT RecordID
FROM Table
WHERE UserID = 1
AND RecordID >= '$LastID'
LIMIT '$RowsAffected'";
$IDResult = mysql_query($IDSQL);
as a follow up to AngeDeLaMort:
You could seperate your inserts and do it something like this:
$data = array (
array(1,2),
array(3,4),
array(5,6)
);
$ids = array();
foreach ($data as $item) {
$sql = 'insert into table (a,b) values ('.$item[0].','.$item[1].')';
mysql_query ($sql);
$id[] = mysql_insert_id();
}
Now all your new id's are in the $id array.
Maybe I can do this
$insert = "insert into table (a,b) values (1,2),(3,4),(5,6)";
$mysqli->query($insert);
$rows_to_be_inserted=3;
$inserted_id = $mysqli->insert_id // gives me the id of the first row in my list
$last_row_id = ($inserted_id+$rows_to_be_inserted)-1;
$mysql->query("select * from table where id between $inserted_id and $last_row_id");
what to you guys say?