Answer the question
In order to leave comments, you need to log in
Why does PDO::lastInsertId return the wrong ID on frequent database inserts?
There is a web application (Win7x64, MS Sql Server 2012, Apache 2.4, PHP 5.6, Yii 1.1).
About a hundred users constantly fill one table. If different users have inserted a row at the same time, the ID of the inserted row of the second is returned to the first and further actions, for example, saving the history of changes, are carried out with an incorrect ID.
Yii uses PDO, in particular the lastInsertId method to get the ID, but I have not seen a description of a similar problem on the Internet.
Why does this happen and how to avoid it?
Thanks in advance.
upd.
Simplified code looks like this:
//Контроллер
public function actionCreate(){
$model = new Pacient();
$model->attributes = $_POST['Pacient'];
$model->save();
}
//Модель
public function beforeSave(){
if (empty($this->NNAPR)) {
if (!$this->setNNAPR())
return false;
}
return parent::beforeSave();
}
public function afterSave(){
parent::afterSave();
$history = new PacientHistory();
//здесь уже неверный ID
$history->ID_PACIENT = $this->ID;
$history->save();
}
CREATE TABLE [dbo].[Collision](
[id] [int] IDENTITY(1,1) NOT NULL,
[uuid] [varchar](50) NULL,
CONSTRAINT [PK_Collision] PRIMARY KEY CLUSTERED
(
[id] ASC
) ON [PRIMARY]
)
CREATE TABLE [dbo].[CollisionHistory](
[id] [int] IDENTITY(1,1) NOT NULL,
[collision_id] [int] NULL,
[uuid] [varchar](50) NULL,
CONSTRAINT [PK_CollisionHistory] PRIMARY KEY CLUSTERED
(
[id] ASC
)
)
public function afterSave(){
parent::afterSave();
$history = new CollisionHistory();
$history->collision_id = $this->id;
$history->uuid = $this->uuid;
$history->save(false);
}
class CollisionController extends CController
{
public function actionTest()
{
for ($i = 0; $i < 1000; $i++) {
$model = new Collision();
$model->uuid = $this->UUIDv4();
$model->save(false);
}
}
//метод с php.net
private function UUIDv4()
{
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
// 16 bits for "time_mid"
mt_rand(0, 0xffff),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
mt_rand(0, 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
mt_rand(0, 0x3fff) | 0x8000,
// 48 bits for "node"
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
}
Answer the question
In order to leave comments, you need to log in
I did a test on pure PDO, changed several versions, but the result is the same. The PDO::lastinsertid method returns the most recent id for the table, like the MSSQL IDENT_CURRENT() function.
That is, if an insert occurred in another session between the insert and getting the id, then the id from the second session is returned.
I solved my problem by creating a stored procedure for inserts into the most frequently used table, in which the output parameter is filled with the SCOPE_IDENTITY () function.
By the way, when using an output parameter in a stored procedure, do not forget about the SET NOCOUNT ON statement, which disables the display of the number of rows affected by the procedure instead of the output parameter.
It's impossible.
lastinsert returns the last entry for the current database connection. Which cannot be in any other user.
So that's not the error.
Unless of course you did not check by opening two tabs in one browser :D
Whether at you such, what the request interposes some records at once?
Then the ID of the most recently inserted
PS will be returned. By the way, the UUIDv4 method used is not the most reliable in terms of key uniqueness
Have you tried wrapping insert requests and getting the last id in a transaction?
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question