I'm trying to get the inserted data from the last multi insert query so I can verify the written data.
I use pdo to execute the query.
$sql="insert into tableName (col1,col2) values (val1,val2),(val4,val5),(val7,val8) ON DUPLICATE KEY UPDATE Col1=VALUES(Col1),Col2=VALUES(Col2)";
$stmt = dbh->prepare($sql);
$stmt->execute();
and then I run the lastInsertId() to get the last id of my autoIncrement column.
$lastId=$dbh->lastInsertId();
and the rowCount() function to get the number of inserted rows (don't care about the duplicates)
$numberOfNewRows=$dbh->rowCount();
now I want to get the data from the previous insert query
$limitRangeStart=$lastId-$numberOfNewRows;
$sql="select * from previusTable limit $limitRangeStart , $lastId ";
$stmt = dbh->prepare($sql);
$stmt->execute();
So my question is will the last query ALWAYS return the data the previously query inserted since multi insert method used?
Is there any possibility that another insert query that might run at that time will "break" the multi insert rows from the previous query?
Related
I am doing bulk insert and inserting 0.5 million tokens in the database with insert "ignore statement". in 0.5 million tokens there can be duplicate tokens.
so if i insert 0.5 million tokens in the database with insert ignore statement then there is no guarantee that all of tokens are inserted into the database because of duplicate tokens.
After doing insertion i want to know how many tokens are inserted into the database. some people are suggesting to use affected_rows columns to get count of inserted (affected) rows. But affected_rows doesn't give the output of current sql statement it gives the output of last sql statement.
Please tell me the best way to get count of inserted rows with insert ignore statment.
Put select row_count(); just after the insert statement to get the number of rows inserted.
eg:
insert ignore into tbl(col1) values (1),(2); select row_count();
Doing a single SQL insert ignore would work with affected_rows. Not sure tho how would that turn out performance wise since it's 0.5 mil rows to enter.
Anyhow, here's a solution I tried and works with 4 values in a signle INSERT.
<?php
$mysqli = new mysqli('127.0.0.1','root','','test');
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$sql = "INSERT IGNORE INTO test1 (Name, Attribute, Val) VALUES ('ai', 'blue', '1j'),('ai1', 'white', '2j'),('ai2', 'black', '3j'),('ai1', 'green', '4j')";
$insert = $mysqli->query($sql);
printf ($mysqli->affected_rows);
?>
I'm need to get the ID of the last record inserted into my table.
INSERT INTO mytable (Column1, Column2) VALUE ('Test', 'Bob');
SELECT/SET LAST_INSERT_ID() as NewID;
Response.Write rst("NewID") '(for example)
Can it run in one statement, or do I need to run the SELECT LAST_INSERT_ID after the INSERT SQL has ran.
I am using MYSQL and ASP
You need to run it after every statment as it will always returns one id of most recent inserted record.
To achieve your goal,
You need to do is, whenever any record inserted to table, get the last id and store it in to any string/table every time and return it from your sproc. This way you get all inserted id at once.
I think that you need to run 2 procedure, first insert and then select order by id desc
$stmt2 = $db->prepare("INSERT INTO
usertabbrige(`tabId`,`uId`)
VALUES
((LAST_INSERT_ID()),$userId)");
anything wrong with this query? It's wrap within my first stmt, which will insert a value into uId (PK) in other table. usertabbrige table contain a field uId which is a FK.
Do not use LAST_INSERT_ID() in your query. You dont know which insert statement was last in current session. You can insert to one table, and if you use LAST_INSERT_ID() in another query, you dont actually know where LAST_INSERT_ID() came from.
As I can see you are using PDO. After you executed an insert query, save id:
$db->query("INSERT INTO ...");
$lastInsertedTabId = $db->lastInsertId;
Use it in your next prepared statement
$stmt2 = $db->prepare("INSERT INTO
usertabbrige(`tabId`,`uId`)
VALUES
($lastInsertedTabId ,$userId)");
I can insert 2 pets into a table, and get their lastInsertId() for further processing one at a time (2 queries). I am wondering if there is a way to get two lastInsertIds() and assign them to variables if I am inserting 2 rows in 1 query:
$query = "INSERT INTO pets (pet_name) VALUES (':coco'),(':jojo')";
$pet_insert = $dbh->prepare($query);
$pet_insert->execute(array(':coco' => $coco,':jojo' => $jojo));
$New_PetID = $dbh->lastInsertId();
Is it possible to get the lastInsertId() for coco and for jojo? So something like:
$New_PetID1 = $dbh->lastInsertId();//coco
$New_PetID2 = $dbh->lastInsertId();//jojo
This will give the same ID, any way to get the 2 IDs? Just for reference, this is in a try block.
It's not possible. If you need generated ids for both rows - you need to perform 2 separated INSERT
Important If you insert multiple rows using a single INSERT statement,
LAST_INSERT_ID() returns the value generated for the first inserted
row only. The reason for this is to make it possible to reproduce
easily the same INSERT statement against some other server.
http://dev.mysql.com/doc/refman/5.5/en/information-functions.html#function_last-insert-id
you can add 1 to last insert id to achieve the real last record id.and if you insert more than 2 record just add rowCount - 1 to last_insert_id.
With innodb_autoinc_lock_mode set to 0 (“traditional”) or 1 (“consecutive”), the auto-increment values generated by any given statement will be consecutive, without gaps, because the table-level AUTO-INC lock is held until the end of the statement, and only one such statement can execute at a time.
and
The ID that was generated is maintained in the server on a per-connection basis. This means that the value returned by the function to a given client is the first AUTO_INCREMENT value generated for most recent statement affecting an AUTO_INCREMENT column by that client. This value cannot be affected by other clients, even if they generate AUTO_INCREMENT values of their own. This behavior ensures that each client can retrieve its own ID without concern for the activity of other clients, and without the need for locks or transactions.
for more info read this document http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html
Please, please avoid the type of solutions given by Bruce. The auto increment value can be set to be different than 1. For instance in case of master to master replication. A little while back a host where one of our applications runs on changed their db setup. I happened to notice -purely by coincidence- that all of a sudden the id's incremented with two instead of one. Would we have had any code like this it could have caused us serious problems.
The documentation states: Returns the ID of the last inserted row or sequence value
You will need to perform two queries to get the id for each inserted row.
I tried to assume that is is not possible until I tried it on my own and figured that IT IS POSSIBLE.
After each execute, add the lastInsertId() and assign a key.
For my example:
// First query
$sql = "INSERT INTO users
(username, email)
values
(:username, :email)";
$sth = $this->db->prepare($sql);
$sth->bindParam(':username', $data['username'], PDO::PARAM_STR);
$sth->bindParam(':email', $data['email'], PDO::PARAM_STR);
// Second Query
$sql2 = "INSERT INTO users
(username, email)
values
(:username, :email)";
$sth2 = $this->db->prepare($sql2);
$sth2->bindParam(':username', $data['username'], PDO::PARAM_STR);
$sth2->bindParam(':email', $data['email'], PDO::PARAM_STR);
// Start trans
$this->db->beginTransaction();
// Execute the two queries
$sth->execute();
$last['ux1'] = $this->db->lastInsertId(); // <---- FIRST KEY
$sth2->execute();
$last['ux2'] = $this->db->lastInsertId(); // <---- SECOND KEY
// Commit
$this->db->commit();
And I was able to retrieve the two last inserted ids
Array (
[create] => Array
(
[ux1] => 117
[ux2] => 118
)
)
I hope this will help others who are seeking the right answer.
Well, since 1000 inserts is a bit longer than 1 insert, the issue of topic is still interesting. Possible workaround would be to make 2 queries. The first one is to insert 1000 rows and the second one is to select them if there is something unique in those inserted rows.
For example with pets:
INSERT INTO pets ( name ) VALUES ( 'coco', 'jojo' );
SELECT id FROM pets WHERE name IN ( 'coco', 'jojo' );
This could give benefit only for big data sets.
I used the array in the first statement and in the second add if condition. So If insert record get its last id then make the second statement. If insert the second array get its last id then repeat the second statement
for ($count=0; $count < count($_POST["laptop_ram_capacity"]); $count++) {
$stmt = $con->prepare('INSERT INTO `ram`
(ram_capacity_id, ram_type_id, ram_bus_id, ram_brand_id, ram_module_id)
VALUES (?,?,?,?,?)');
//excute query//
$stmt->execute(array(
$_POST['laptop_ram_capacity'][$count],
$_POST["laptop_ram_type"][$count],
$_POST["laptop_ram_bus"][$count],
$_POST["laptop_ram_brand"][$count],
$_POST["laptop_ram_module"][$count]
));
//fetsh the data//
$rows = $stmt->rowCount();
// i add the below statment in if condition to repeat the insert and pass the repeated of the last statement//
if ($rows > 0) {
$LASTRAM_ID = $con->lastInsertId();
$stmt = $con->prepare('INSERT INTO `ram_devicedesc_rel`
(ram_id, Parent_device_description_id)
VALUES (?,?)');
//excute query//
$stmt->execute(array(
$LASTRAM_ID,
$LASTdevicedesc_ID
));
//fetsh the data//
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
}
you can use 'multi_query()'
add all the query into one variable like :
'$sql1 = "first insert";'
'$sql2 = "second insert";'
'$sql3 = "third insert";' and so on...
take the count of number of inserts your gonna make.
now execute all the '$sql' queries using 'multi_query()'.
get the last insert id.
now all you have to do is '$rowno = $lastinsertid - $countofinserts'.
so basically you will get a number.
add 1 to it and from the resulting number to the lastinsertid are the insert id's of the insert queries you ran
It is possible! All you need to do is call the PDO lastInsertId() method. This will return the first inserted id when doing multiple or bulk INSERT. Next we need to perform a simple addition subtraction operation with the number of rows affected by the last INSERT statement.
$firstInsertedId = $this->lastInsertId();
$InsertedIds = range($firstInsertedId, ($firstInsertedId + ($stmt->rowCount() - 1)) );
print_r($InsertedIds);
If I insert multiple records with a loop that executes a single record insert, the last insert id returned is, as expected, the last one. But if I do a multiple records insert statement:
INSERT INTO people (name,age)
VALUES ('William',25), ('Bart',15), ('Mary',12);
Let's say the three above are the first records inserted in the table. After the insert statement I expected the last insert id to return 3, but it returned 1. The first insert id for the statement in question.
So can someone please confirm if this is the normal behavior of LAST_INSERT_ID() in the context of multiple records INSERT statements. So I can base my code on it.
Yes. This behavior of last_insert_id() is documented in the MySQL docs:
Important
If you insert multiple rows using a single INSERT statement, LAST_INSERT_ID() returns the value generated for the first inserted row only. The reason for this is to make it possible to reproduce easily the same INSERT statement against some other server.
This behavior is mentioned on the man page for MySQL. It's in the comments but is not challenged, so I'm guessing it's the expected behavior.
I think it's possible if your table has unique autoincrement column (ID) and you don't require them to be returned by mysql itself. I would cost you 3 more DB requests and some processing. It would require these steps:
Get "Before MAX(ID)" right before your insert:
SELECT MAX(id) AS before_max_id FROM table_name`
Make multiple INSERT ... VALUES () query with your data and keep them:
INSERT INTO table_name
(col1, col2)
VALUES
("value1-1" , "value1-2"),
("value2-1" , "value2-2"),
("value3-1" , "value3-2"),
ON DUPLICATE KEY UPDATE
Get "After MAX(ID)" right after your insert:
SELECT MAX(id) AS after_max_id FROM table_name`
Get records with IDs between "Before MAX(ID)" and "After MAX(ID)" including:
SELECT * FROM table_name WHERE id>$before_max_id AND id<=$after_max_id`
Do a check of retrieved data with data you inserted to match them and remove any records that were not inserted by you. The remaining records have your IDs:
foreach ($after_collection as $after_item) {
foreach ($input_collection as $input_item) {
if ( $after_item->compare_content($input_item) ) {
$intersection_array[] = $after_item;
}
}
}
This is just how a common person would solve it in a real world, with parts of code. Thanks to autoincrement it should get smallest possible amount of records to check against, so they will not take lot of processing. This is not the final "copy & paste" code - eg. you have to create your own function compare_content() according you your needs.