通过php数组将多行插入mysql

时间:2009-04-23 01:39:06

标签: php mysql codeigniter insert bulkinsert

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

14 个答案:

答案 0 :(得分:222)

在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()将values子句赋值给外部数组。

答案 1 :(得分:58)

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')

答案 2 :(得分:19)

您可以使用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)。

答案 3 :(得分:14)

我知道这是一个旧查询,但我只是在阅读并认为我会添加我在其他地方找到的内容:

PHP 5中的mysqli是一个具有一些好功能的ojbect,可以让你加快上面答案的插入时间:

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

当插入很多行时关闭自动提交会大大加快插入速度,所以关闭它,然后执行如上所述,或者只是创建一个字符串(sqlCombined),这是由分号分隔的多个insert语句,多个查询将处理他们很好。

希望这可以帮助别人节省时间(搜索和插入!)

[R

答案 4 :(得分:7)

你总是可以使用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语句。

答案 5 :(得分:5)

好吧,你不想执行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()将内容转储到数组一样简单。

答案 6 :(得分:3)

$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));

答案 7 :(得分:1)

你可以在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));

答案 8 :(得分:0)

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

$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;
    }
}

答案 9 :(得分:0)

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

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

答案 10 :(得分:0)

我创建了这个简单的功能,你们可以轻松使用。您需要针对插入数据,数据数组($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));
}

答案 11 :(得分:0)

虽然回答这个问题已经太晚了。以下是我的答案。

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

<强> $这 - &GT; DB-&GT; 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

的更多详细信息

答案 12 :(得分:0)

我不得不将 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 :(得分:-4)

use this in codeigniter for multiple data insertion


 $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')