【问题标题】:How to insert multiple rows from array using CodeIgniter framework?如何使用 CodeIgniter 框架从数组中插入多行?
【发布时间】:2010-10-21 06:25:36
【问题描述】:

我正在使用插入命令通过 PHP 将一个大型数据集传递到 MySQL 表中,我想知道是否可以通过查询一次插入大约 1000 行,而不是在一英里末尾附加每个值 -长字符串,然后执行它。我正在使用 CodeIgniter 框架,因此我也可以使用它的功能。

【问题讨论】:

  • 我已经根据你对 Codeigniter 的多行插入的问题给出了答案。
  • @SomnathMuluk 谢谢,但是我已经有一段时间没有需要回答这个问题了 :)...
  • 我会推荐使用 CodeIgniter 的 insert_batch 函数。如果您使用库,请始终尝试利用它的优势和编码标准。
  • 我相信插入批处理是最好的方法,请参阅链接stackoverflow.com/questions/27206178/codeigniter-insert-batch

标签: php mysql codeigniter insert bulkinsert


【解决方案1】:

在 MySQL 中将一条 INSERT 语句与多行组合起来比每行一条 INSERT 语句要快得多。

也就是说,听起来您可能会在 PHP 中遇到字符串处理问题,这实际上是算法问题,而不是语言问题。基本上,在处理大字符串时,您希望尽量减少不必要的复制。首先,这意味着您要避免串联。构建大字符串(例如一次插入数百行)的最快和最节省内存的方法是利用 implode() 函数和数组分配。

$sql = array(); 
foreach( $data as $row ) {
    $sql[] = '("'.mysql_real_escape_string($row['text']).'", '.$row['category_id'].')';
}
mysql_query('INSERT INTO table (text, category) VALUES '.implode(',', $sql));

这种方法的优点是您无需复制和重新复制到目前为止已与每个连接一起组装的 SQL 语句;相反,PHP 在implode() 语句中这样做一次。这是一个的胜利。

如果您有很多列要放在一起,并且一个或多个很长,您还可以构建一个内部循环来执行相同的操作,并使用implode() 将值子句分配给外部数组。

【讨论】:

  • 谢谢!顺便说一句,如果有人打算复制它,您在函数末尾缺少一个右括号。 mysql_real_query('INSERT INTO table VALUES (text, category) '.implode(','.$sql));
  • 谢谢!固定的。 (我经常这样做……)
  • 我相信这段代码将为我的最新项目创建一个解决方案。我的问题是,这对 SQL 注入安全吗?我的计划是用mysqli_real_escape_stringmysql_query 换掉mysql_real_escape_stringmysqli_query,因为我使用的是MySQLi,这些从PHP5 开始已被弃用。非常感谢!
  • 这个查询容易被sql注入!
  • mysql_* 已从 PHP 中删除,因此请务必使用 mysqli_* 接口。
【解决方案2】:

CodeIgniter 现在支持多插入/批量插入。

$data = array(
   array(
      'title' => 'My title' ,
      'name' => 'My Name' ,
      'date' => 'My date'
   ),
   array(
      'title' => 'Another title' ,
      'name' => 'Another Name' ,
      'date' => 'Another date'
   )
);

$this->db->insert_batch('mytable', $data);

// Produces: INSERT INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date'), ('Another title', 'Another name', 'Another date')

【讨论】:

  • 我认为这是最推荐的多行插入方式,而不是使用 mysql_query。因为当我们使用框架时,最好始终使用框架的内置功能。
【解决方案3】:

您可以使用 mysqli_stmt 类准备查询以插入一行,然后遍历数据数组。比如:

$stmt =  $db->stmt_init();
$stmt->prepare("INSERT INTO mytbl (fld1, fld2, fld3, fld4) VALUES(?, ?, ?, ?)");
foreach($myarray as $row)
{
    $stmt->bind_param('idsb', $row['fld1'], $row['fld2'], $row['fld3'], $row['fld4']);
    $stmt->execute();
}
$stmt->close();

其中 'idsb' 是您要绑定的数据的类型(int、double、string、blob)。

【讨论】:

  • 我最近运行了一些基准测试,比较了这里提到的批量插入和准备好的插入语句。对于大约 500 个插入,准备好的插入方法在 2.6-4.4 秒之间完成,批量插入方法在 0.12-0.35 秒之间完成。我原以为 mysql 会将这些准备好的语句“打包”在一起,并且性能与批量插入一样好,但是在默认设置中,性能差异显然是巨大的。 (顺便说一句,所有基准查询都在每个测试的单个事务中运行,以防止自动提交)
【解决方案4】:

PHP 5 中的 mysqli 是一个具有一些良好功能的对象,可以让您加快上述答案的插入时间:

$mysqli->autocommit(FALSE);
$mysqli->multi_query($sqlCombined);
$mysqli->autocommit(TRUE);

在插入多行时关闭自动提交大大加快了插入速度,所以关闭它,然后按照上面所说的执行,或者只是制作一个字符串(sqlCombined),它是由分号分隔的许多插入语句和多查询将处理他们很好。

【讨论】:

  • 这是我使用您的想法时遇到的错误:“致命错误:在第 30 行的 /homepages/25/d402746174/htdocs/MoneyMachine/saveQuotes.php 中的 null 上调用成员函数 autocommit() "
【解决方案5】:

你总是可以使用mysql的LOAD DATA:

LOAD DATA LOCAL INFILE '/full/path/to/file/foo.csv' INTO TABLE `footable` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n' 

进行批量插入,而不是使用一堆 INSERT 语句。

【讨论】:

  • 我已经调查过了,但我需要在插入数据之前对其进行操作。它是作为 1400 x 1400 组 int 值的笛卡尔积而提供给我的,其中许多值为零。我需要使用中间表将其转换为多对多关系以节省空间,因此需要插入而不是批量插入
  • 你总是可以在操作它并调用加载数据的mysql语句后生成一个csv文件
  • 我认为知道路径在 SQL 客户端本地而不是在 SQL 服务器上会很有帮助。该文件被上传到服务器,然后被它读取。我认为该文件必须已经在服务器上,但事实并非如此。如果它已经在服务器上,请删除LOCAL 位。
【解决方案6】:

好吧,您不想执行 1000 次查询调用,但这样做很好:

$stmt= array( 'array of statements' );
$query= 'INSERT INTO yourtable (col1,col2,col3) VALUES ';
foreach( $stmt AS $k => $v ) {
  $query.= '(' .$v. ')'; // NOTE: you'll have to change to suit
  if ( $k !== sizeof($stmt)-1 ) $query.= ', ';
}
$r= mysql_query($query);

根据您的数据源,填充数组可能就像打开文件并通过 file() 将内容转储到数组中一样简单。

【讨论】:

  • 如果将 if 移到查询上方并将其更改为 if ($k>0) 之类的内容会更干净。
  • @cherouvim...嗯,你说得对。感谢您的输入。当我重新阅读我提供的示例时,我没有看到你的意思。注意详细说明(通过pastebin等?)。谢谢-
【解决方案7】:
$query= array(); 
foreach( $your_data as $row ) {
    $query[] = '("'.mysql_real_escape_string($row['text']).'", '.$row['category_id'].')';
}
mysql_query('INSERT INTO table (text, category) VALUES '.implode(',', $query));

【讨论】:

    【解决方案8】:

    您可以在 codeigniter 中通过多种方式进行操作,例如

    首先循环

    foreach($myarray as $row)
    {
       $data = array("first"=>$row->first,"second"=>$row->sec);
       $this->db->insert('table_name',$data);
    }
    

    第二 -- 批量插入

    $data = array(
           array(
              'first' => $myarray[0]['first'] ,
              'second' => $myarray[0]['sec'],
            ),
           array(
              'first' => $myarray[1]['first'] ,
              'second' => $myarray[1]['sec'],
            ),
        );
    
        $this->db->insert_batch('table_name', $data);
    

    第三种方式——多值传递

    $sql = array(); 
    foreach( $myarray as $row ) {
        $sql[] = '("'.mysql_real_escape_string($row['first']).'", '.$row['sec'].')';
    }
    mysql_query('INSERT INTO table (first, second) VALUES '.implode(',', $sql));
    

    【讨论】:

    • 这三个中哪个最快?
    • BTW mysql_query 很快,但你知道这种类型的编码是 CORE PHP 的一部分,而不是框架。所以你必须使用 codigniter 中的 insert_batch 方法。
    【解决方案9】:

    虽然回答这个问题为时已晚。这是我的回答。

    如果您使用的是 CodeIgniter,那么您可以使用在 query_builder 类中定义的内置方法。

    $this->db->insert_batch()

    根据您提供的数据生成插入字符串,并运行查询。您可以将数组或对象传递给函数。下面是一个使用数组的例子:

    $data = array(
        array(
                'title' => 'My title',
                'name' => 'My Name',
                'date' => 'My date'
        ),
        array(
                'title' => 'Another title',
                'name' => 'Another Name',
                'date' => 'Another date'
        )
    );
    
    $this->db->insert_batch('mytable', $data);
    // Produces: INSERT INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date'),  ('Another title', 'Another name', 'Another date')
    

    第一个参数将包含表名,第二个参数是值的关联数组。

    您可以找到有关 query_builder here 的更多详细信息

    【讨论】:

      【解决方案10】:

      我创建了一个执行多行的类,使用如下:

      $pdo->beginTransaction();
      $pmi = new PDOMultiLineInserter($pdo, "foo", array("a","b","c","e"), 10);
      $pmi->insertRow($data);
      // ....
      $pmi->insertRow($data);
      $pmi->purgeRemainingInserts();
      $pdo->commit();
      

      其中类的定义如下:

      class PDOMultiLineInserter {
          private $_purgeAtCount;
          private $_bigInsertQuery, $_singleInsertQuery;
          private $_currentlyInsertingRows  = array();
          private $_currentlyInsertingCount = 0;
          private $_numberOfFields;
          private $_error;
          private $_insertCount = 0;
      
          /**
           * Create a PDOMultiLine Insert object.
           *
           * @param PDO $pdo              The PDO connection
           * @param type $tableName       The table name
           * @param type $fieldsAsArray   An array of the fields being inserted
           * @param type $bigInsertCount  How many rows to collect before performing an insert.
           */
          function __construct(PDO $pdo, $tableName, $fieldsAsArray, $bigInsertCount = 100) {
              $this->_numberOfFields = count($fieldsAsArray);
              $insertIntoPortion = "REPLACE INTO `$tableName` (`".implode("`,`", $fieldsAsArray)."`) VALUES";
              $questionMarks  = " (?".str_repeat(",?", $this->_numberOfFields - 1).")";
      
              $this->_purgeAtCount = $bigInsertCount;
              $this->_bigInsertQuery    = $pdo->prepare($insertIntoPortion.$questionMarks.str_repeat(", ".$questionMarks, $bigInsertCount - 1));
              $this->_singleInsertQuery = $pdo->prepare($insertIntoPortion.$questionMarks);
          }
      
          function insertRow($rowData) {
              // @todo Compare speed
              // $this->_currentlyInsertingRows = array_merge($this->_currentlyInsertingRows, $rowData);
              foreach($rowData as $v) array_push($this->_currentlyInsertingRows, $v);
              //
              if (++$this->_currentlyInsertingCount == $this->_purgeAtCount) {
                  if ($this->_bigInsertQuery->execute($this->_currentlyInsertingRows) === FALSE) {
                      $this->_error = "Failed to perform a multi-insert (after {$this->_insertCount} inserts), the following errors occurred:".implode('<br/>', $this->_bigInsertQuery->errorInfo());
                      return false;
                  }
                  $this->_insertCount++;
      
                  $this->_currentlyInsertingCount = 0;
                  $this->_currentlyInsertingRows = array();
              }
              return true;
          }
      
          function purgeRemainingInserts() {
              while ($this->_currentlyInsertingCount > 0) {
                  $singleInsertData = array();
                  // @todo Compare speed - http://www.evardsson.com/blog/2010/02/05/comparing-php-array_shift-to-array_pop/
                  // for ($i = 0; $i < $this->_numberOfFields; $i++) $singleInsertData[] = array_pop($this->_currentlyInsertingRows); array_reverse($singleInsertData);
                  for ($i = 0; $i < $this->_numberOfFields; $i++)     array_unshift($singleInsertData, array_pop($this->_currentlyInsertingRows));
      
                  if ($this->_singleInsertQuery->execute($singleInsertData) === FALSE) {
                      $this->_error = "Failed to perform a small-insert (whilst purging the remaining rows; the following errors occurred:".implode('<br/>', $this->_singleInsertQuery->errorInfo());
                      return false;
                  }
                  $this->_currentlyInsertingCount--;
              }
          }
      
          public function getError() {
              return $this->_error;
          }
      }
      

      【讨论】:

        【解决方案11】:

        在codeigniter中使用insert batch插入多行数据。

        $this->db->insert_batch('tabname',$data_array); // $data_array holds the value to be inserted
        

        【讨论】:

          【解决方案12】:

          我不得不在一个表中插入超过 14000 行,发现使用 Mysqli 准备好的语句的每一行花费了十多分钟,而使用相同 Mysqli 准备好的语句的字符串参数的参数解包在不到 10 秒的时间内完成。我的数据非常重复,因为它是 id 的倍数和一个常量整数。

          10分钟代码:

                      $num = 1000;
                      $ent = 4;
                      $value = ['id' => 1,
                                'id' => 2,
                                'id' => 3,
                                'id' => 4,
                                'id' => 5,
                                'id' => 6,
                                'id' => 7,
                                'id' => 8,
                                'id' => 9,
                                'id' => 10,
                                'id' => 11,
                                'id' => 12,
                                'id' => 13,
                                'id' => 14];
                      $cnt = 0;
                      $query = "INSERT INTO table (col1, col2) VALUES (?,?)";
                      $stmt = $this->db->prepare($query);
                      $stmt->bind_param('ii', $arg_one,$arg_two);
                              foreach ($value as $k => $val) {
                                   for ($i=0; $i < $num; $i++) { 
                                      $arg_one = $k;
                                      $arg_two = $ent;
                                      if($stmt->execute()) {
                                          $cnt++;
                                      }
                                  }
                              }
          

          10 秒代码:

                      $ent = 4;
                      $num = 1000;
                      $value = ['id' => 1,
                                'id' => 2,
                                'id' => 3,
                                'id' => 4,
                                'id' => 5,
                                'id' => 6,
                                'id' => 7,
                                'id' => 8,
                                'id' => 9,
                                'id' => 10,
                                'id' => 11,
                                'id' => 12,
                                'id' => 13,
                                'id' => 14];
                       $newdat = [];
                       foreach ($value as $k => $val) {
                           for ($i=0; $i < $num; $i++) {
                              $newdat[] = $val;
                              $newdat[] = $ent;
                           }
                       }
                      // create string of data types
                      $cnt = count($newdat);
                      $param = str_repeat('i',$cnt);
                      // create string of question marks
                      $rec = (count($newdat) == 0) ? 0 : $cnt / 2 - 1;
                      $id_q = str_repeat('(?,?),', $rec) . '(?,?)';
                      // insert
                      $query = "INSERT INTO table (col1, col2) VALUES $id_q";
                      $stmt = $db->prepare($query);
                      $stmt->bind_param($param, ...$newdat);
                      $stmt->execute();
          

          【讨论】:

            【解决方案13】:

            我创建了这个简单的功能,你们可以轻松使用。您需要将表名($tbl)、表字段($insertFieldsArr) 传递给插入数据、数据数组($arr)

            insert_batch('table',array('field1','field2'),$dataArray);
            
                function insert_batch($tbl,$insertFieldsArr,$arr){ $sql = array(); 
                foreach( $arr as $row ) {
                    $strVals='';
                    $cnt=0;
                    foreach($insertFieldsArr as $key=>$val){
                        if(is_array($row)){
                            $strVals.="'".mysql_real_escape_string($row[$cnt]).'\',';
                        }
                        else{
                            $strVals.="'".mysql_real_escape_string($row).'\',';
                        }
                        $cnt++;
                    }
                    $strVals=rtrim($strVals,',');
                    $sql[] = '('.$strVals.')';
                }
            
                $fields=implode(',',$insertFieldsArr);
                mysql_query('INSERT INTO `'.$tbl.'` ('.$fields.') VALUES '.implode(',', $sql));
            }
            

            【讨论】:

              猜你喜欢
              • 2020-08-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-01-14
              • 2021-03-07
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多