5

我在 CodeIgniter 中运行一个小方法来在数据库(同一张表)中插入一些行。我想看看事务中哪个插入失败(通过返回一个标题数组)。我的代码是:

$failure = array(); //the array where we store what failed
$this->db->trans_start();
foreach ($data as $ressourceCsv){ //data is an array of arrays to feed the database
    $this->ajout_ressource($ressourceCsv); //method to insert (basically, just an insert with active record)
    if (($this->db->_error_message())!=null) { 
          $failure[] = $ressourceCsv['title']; 
    }
}
$this->db->trans_complete();
return $failure;

事实是,如果我不让它成为一个事务(没有 $this->db->trans_...),它会完美运行,并且我有一个包含一些标题的数组。但是对于事务,数组包含自第一个错误以来的所有标题。有没有办法从导致事务回滚的插入中获取标题?

我也尝试过:

$failure = array(); //the array where we store what failed
$this->db->trans_start();
foreach ($data as $ressourceCsv){ //data is an array of arrays to feed the database

    if (!$this->ajout_ressource($ressourceCsv)) { //active record insertion return true
          $failure[] = $ressourceCsv['title'];   // if successful
    }
}
$this->db->trans_complete();
return $failure;
4

1 回答 1

3

我相信一旦事务内部发生错误,您必须回滚,然后才能制作更多的数据库模块。这将解释您所看到的行为。在第一个错误之后,事务被“中止”并且您继续循环,导致每个后续 SQL 命令也失败。这可以说明如下:

db=# select * from test1;
 id | foo | bar
----+-----+-----
(0 rows)

db=# begin;
BEGIN
db=# insert into test1 (foo, bar) values (1, 'One');
INSERT 0 1
db=# insert into test1 (foo, bar) values (Oops);
ERROR:  column "oops" does not exist
LINE 1: insert into test1 (foo, bar) values (Oops);
                                             ^
db=# insert into test1 (foo, bar) values (2, 'Two');
ERROR:  current transaction is aborted, commands ignored until end of transaction block
db=# select * from test1;
ERROR:  current transaction is aborted, commands ignored until end of transaction block
db=# commit;
ROLLBACK
ace_db=# select * from test1;
 id | foo | bar
----+-----+-----
(0 rows)

db=#

请注意,如果出现错误,“提交”似乎会“回滚”(这不是错字。)

顺便说一句:用于$this->db->trans_status() === FALSE检查交易过程中的错误。

更新:这里有一些(未经测试的)代码可以在事务中执行,这样在您准备好之前插入不会被其他人看到:

$failure = array(); //the array where we store what failed
$done = false;
do {
    $this->db->trans_begin();
    foreach ($data as $key => $ressourceCsv){ //data is an array of arrays to feed the database
        $this->ajout_ressource($ressourceCsv); //method to insert (basically, just an insert with active record)
        if ($this->db->trans_status() === false) { // an insert failed
            $failure[] = $ressourceCsv['title'];   // save the failed title
            unset($data[$key]);                    // remove failed insert from data set
            $this->db->trans_rollback();           // rollback the transaction
            break;                                 // retry the insertion
        }
    }
    $done = true;                                  // completed without failure
} while (count($data) and ! $done);                // keep going until no data or success

/*
 * Two options (uncomment one):
 * 1. Commit the successful inserts even if there were failures.

$this->db->trans_commit();

 * 2. Commit the successful inserts only if no failures.

if (count($failure)) {
    $this->db->trans_rollback();
} else {
    $this->db->trans_commit();
}
*/

return $failure;
于 2013-07-01T19:41:54.023 回答