Activerecord 批量插入Yii

Activerecord 批量插入Yii,activerecord,yii,insert,yii1.x,Activerecord,Yii,Insert,Yii1.x,我需要在Yii中插入多个ActiveRecord对象(如果所有对象都已插入) $transaction = Yii::app()->db->beginTransaction(); for ($i = 0;$i < 10;$i++){ $model = new Mymodel(); $model->x = $i; if (!$model->save()){ $transaction->rollback();

我需要在Yii中插入多个ActiveRecord对象(如果所有对象都已插入)

$transaction = Yii::app()->db->beginTransaction();
for ($i = 0;$i < 10;$i++){
    $model = new Mymodel();
    $model->x = $i;
    if (!$model->save()){
        $transaction->rollback();
        break;
    }
}
if ($transaction->active)
    $transaction->commit();
$transaction=Yii::app()->db->beginTransaction();
对于($i=0;$i<10;$i++){
$model=新的Mymodel();
$model->x=$i;
如果(!$model->save()){
$transaction->rollback();
打破
}
}
如果($transaction->active)
$transaction->commit();

现在,我需要在一个查询中插入所有数据,在使用活动记录期间如何执行此操作?

虽然不完全是我喜欢的,但它可以作为扩展/组件,并被视为普通命令,因此事务仍然适用。完全可以将此设置为在查询中使用参数而不是字符串文本,还可以实现空值和默认值的检查

class CDbMultiInsertCommand extends CDbCommand{

/** @var CActiveRecord $class */
private $class;

/** @var string $insert_template */
private $insert_template = "insert into %s(%s) ";

/** @var string $value_template */
private $value_template = "(%s)";

/** @var string $query */
public $query;

/** @var CDbColumnSchema[] $columns */
private $columns;

/** @var boolean $fresh */
private $fresh;

/** @var CDbConnection $db */
private $db;

/** @param CActiveRecord $class
 *  @param CDbConnection $db
 */
public function __construct($class, $db = null){
  $this->class = $class;
  $this->createTemplate();
  if(is_null($db)){
    $this->db = Yii::app()->db;
  }
  else{
    $this->db = $db;
  }
}
private function createTemplate(){
  $this->fresh = true;
  $value_template = "";
  $columns_string = "";
  $this->columns = $this->class->getMetaData()->tableSchema->columns;
  $counter = 0;
  foreach($this->columns as $column){
    /** @var CDbColumnSchema $column */
    if($column->autoIncrement){
      $value_template .= "0";
    }
    else if($column->type == "integer" || $column->type == "boolean" || $column->type == "float" || $column->type == "double") {
      $value_template .= "%d";
    }
    else{
      $value_template .= "\"%s\"";
    }
    $columns_string .= $column->name;
    $counter ++;
    if($counter != sizeof($this->columns)){
      $columns_string .= ", ";
      $value_template .= ", ";
    }
  }

  $this->insert_template = sprintf($this->insert_template, $this->class->tableName(), $columns_string);
  $this->value_template = sprintf($this->value_template, $value_template);
}

/** @param boolean $validate
 *  @param CActiveRecord $record
 */
public function add($record, $validate = true){
  $values = array();
  if($validate){
    if(!$record->validate()){
      return false;
    }
  }
  $counter = 0;
  foreach($this->columns as $column){
    if($column->autoIncrement){
      continue;
    }
    $values[$counter] = $this->class->{$column->name};
    $counter ++;
  }
  if(!$this->fresh){
    $this->query .= ",";
  }
  else{
    $this->query = "values";
  }
  $this->fresh = false;
  $this->query .= vsprintf($this->value_template, $values);
  return true;
}

public function getConnection(){
  return $this->db;
}

public function execute(){
  $this->setText($this->insert_template." ".$this->query);
  return parent::execute();
}
}
用途如下:

$transaction = Yii::app()->db->beginTransaction();
$multi = new CDbMultiInsertCommand(new Mymodel());
for ($i = 0;$i < 10;$i++){
    $model = new Mymodel();
    $model->x = $i;
    $multi->add($model, $shouldBeValidated);
}

$multi->execute();

if ($transaction->active)
    $transaction->commit();
$transaction=Yii::app()->db->beginTransaction();
$multi=新的CDbMultiInsertCommand(新的Mymodel());
对于($i=0;$i<10;$i++){
$model=新的Mymodel();
$model->x=$i;
$multi->add($model,$shouldbeevalidate);
}
$multi->execute();
如果($transaction->active)
$transaction->commit();
当然,它可以更加详细和扩展,以允许更新等


希望这有帮助。

这门课的新版本

class CDbMultiInsertCommand extends CDbCommand{

    /** @var CActiveRecord $class */
    private $class;

    /** @var string $insert_template */
    private $insert_template = "insert into %s(%s) ";

    /** @var string $value_template */
    private $value_template = "(%s)";

    /** @var string $query */
    public $query;

    /** @var CDbColumnSchema[] $columns */
    private $columns;

    /** @var boolean $fresh */
    private $fresh;

    /** @var CDbConnection $db */
    private $db;

    /** @param CActiveRecord $class
     *  @param CDbConnection $db
     */
    public function __construct($class, $db = null){


        $this->class = $class;
        $this->createTemplate();
        if(is_null($db)){
            $this->db = Yii::app()->db;
        }
        else{
            $this->db = $db;
        }

        parent::__construct($this->getConnection());
    }
    private function createTemplate(){
        $this->fresh = true;
        $value_template = "";
        $columns_string = "";
        $this->columns = $this->class->getMetaData()->tableSchema->columns;
        $counter = 0;
        foreach($this->columns as $column){
            /** @var CDbColumnSchema $column */
            if($column->autoIncrement){
                $value_template .= "0";
            }
            else if($column->type == "integer" || $column->type == "boolean" || $column->type == "float" || $column->type == "double") {
                $value_template .= "%d";
            }
            else{
                $value_template .= "\"%s\"";
            }
            $columns_string .= $column->name;
            $counter ++;
            if($counter != sizeof($this->columns)){
                $columns_string .= ", ";
                $value_template .= ", ";
            }
        }

        $this->insert_template = sprintf($this->insert_template, $this->class->tableName(), $columns_string);
        $this->value_template = sprintf($this->value_template, $value_template);
    }

    /** @param boolean $validate
     *  @param CActiveRecord $record
     */
    public function add($record, $validate = true){
        $values = array();
        if($validate){
            if(!$record->validate()){
                return false;
            }
        }
        $counter = 0;
        foreach($this->columns as $column){
            if($column->autoIncrement){
                continue;
            }
            $values[$counter] = $record->{$column->name};
            $counter ++;
        }
        if(!$this->fresh){
            $this->query .= ",";
        }
        else{
            $this->query = "values";
        }

        $this->fresh = false;
        $this->query .= vsprintf($this->value_template, $values);
        return true;
    }

    public function getConnection(){
        return $this->db;
    }

    public function execute(){
        if(!$this->query)
            return;

        $this->setText($this->insert_template." ".$this->query);
        return parent::execute();
    }
}
用途如下:

$transaction = Yii::app()->db->beginTransaction();
$multi = new CDbMultiInsertCommand(new Mymodel());
for ($i = 0;$i < 10;$i++){
    $model = new Mymodel();
    $model->x = $i;
    $multi->add($model, $shouldBeValidated);
}

$multi->execute();

if ($transaction->active)
    $transaction->commit();
$transaction=Yii::app()->db->beginTransaction();
$multi=新的CDbMultiInsertCommand(新的Mymodel());
对于($i=0;$i<10;$i++){
$model=新的Mymodel();
$model->x=$i;
$multi->add($model,$shouldbeevalidate);
}
$multi->execute();
如果($transaction->active)
$transaction->commit();

批量插入/批量插入YII的更新

class CDbMultiInsertCommand extends CDbCommand{

/** @var CActiveRecord $class */
private $class;

/** @var string $insert_template */
private $insert_template = "insert into %s(%s) ";

/** @var string $value_template */
private $value_template = "(%s)";

/** @var string $query */
public $query;

/** @var CDbColumnSchema[] $columns */
private $columns;

/** @var boolean $fresh */
private $fresh;

/** @var CDbConnection $db */
private $db;

/** @param CActiveRecord $class
 *  @param CDbConnection $db
 */
public function __construct($class, $db = null){


    $this->class = $class;
    $this->createTemplate();
    if(is_null($db)){
        $this->db = Yii::app()->db;
    }
    else{
        $this->db = $db;
    }

    parent::__construct($this->getConnection());
}
private function createTemplate(){
    $this->fresh = true;
    $value_template = "";
    $columns_string = "";
    $this->columns = $this->class->getMetaData()->tableSchema->columns;

    $counter = 0;
    foreach($this->columns as $keyColumnName => $column){
        /** @var CDbColumnSchema $column */
        if($column->autoIncrement){
            unset($this->columns[$keyColumnName]);
            continue;
            // $value_template .= "0";
        }
        else if($column->type == "integer" || $column->type == "boolean" || $column->type == "float" || $column->type == "double") {
            $value_template .= "%d";
        }
        else{
            $value_template .= "\"%s\"";
        }
        $columns_string .= '"'.$column->name.'"';
        $counter ++;
        if($counter != sizeof($this->columns)){
            $columns_string .= ", ";
            $value_template .= ", ";
        }
    }
    $this->insert_template = sprintf($this->insert_template, $this->class->tableName(), $columns_string);
    $this->value_template = sprintf($this->value_template, $value_template);
}

/** @param boolean $validate
 *  @param CActiveRecord $record
 */
public function add($record, $validate = true){
    $values = array();
    if($validate){
        if(!$record->validate()){
            return false;
        }
    }
    $counter = 0;
    foreach($this->columns as $column){
        if($column->autoIncrement){
            continue;
        }
        $values[$counter] = $record->{$column->name};
        $counter ++;
    }
    if(!$this->fresh){
        $this->query .= ",";
    }
    else{
        $this->query = "values";
    }
    $this->fresh = false;
    $this->query .= vsprintf($this->value_template, $values);
    $this->query = str_replace('"', "'", $this->query);

    return true;
}

public function getConnection(){
    return $this->db;
}

public function execute($params=array()){
    if(!$this->query)
        return;

    $this->setText($this->insert_template." ".$this->query);

    return parent::execute();
}
}

我在前面的代码中遇到了3个问题

  • 自动递增列,其中在早期代码中设置为0
  • 具有双引号的查询语句
  • 执行函数应类似于带参数的父执行函数
  • 我想前两点与我使用的postgresql数据库有关,希望更新的代码适用于所有数据库系统。

    因为Yii 1.1.14有可用的方法。如果您需要在一个查询中插入多个记录,您可能应该使用它,因为这个问题中的所有其他答案都容易受到的影响,因此如果您试图自己实现类似的内容,很容易搞糟

    Yii::app()->db->getCommandBuilder()
        ->createMultipleInsertCommand('table_name', $data)
        ->execute();
    
    对于模型数组,您可能可以通过这种方式生成
    $data
    (注意,它不会进行任何验证):


    您不能使用ActiveRecord。@MichaelHartl:没有手工编写查询的其他方法吗?您可以使用或。但是,它们都不会解除您手动编写插入的负担。为什么需要将它们全部作为单个查询插入?当然,使用事务的方式与您的方式几乎完全相同,您可以将其作为单独的查询插入,但仍然可以在必要时回滚,@ZackNewsham:bulk insert有一些好处,执行查询比执行查询更快,它使用的php和数据库之间的网络更少,等等。我明白了这个问题,CDbMultiInsertCommand::execute()的声明应该与CDbCommand::execute($params=Array)@MohdShahid兼容,只需在扩展的
    execute
    函数的参数中添加$param。看起来是SQL注入的好方法(与此问题中的其他答案相同)。。。
    $data = [];
    foreach ($models as $model) {
        $data[] = $model->getAttributes();
    }