通过PHP数组插入多行到MySQL


129

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


根据您对Codeigniter的多行插入的问题,我已经给出了答案。
Somnath Muluk

@SomnathMuluk谢谢,但是已经有一段时间了,因为我需要回答这个问题:)...
toofarsideways

我建议使用CodeIgniter的insert_batch函数。如果使用库,请始终尝试利用其优势和编码标准。
德瓦尔德·埃尔斯

我认为插入批处理是执行此操作的最佳方法,请参见链接stackoverflow.com/questions/27206178/codeigniter-insert-batch
Syed Amir Bukhari,

Answers:


234

INSERT在MySQL中,用多行汇编一条语句要比一条语句快得多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子句分配给外部数组。


5
感谢那!顺便说一句,如果有人打算复制该函数,则在函数末尾缺少一个右括号。mysql_real_query('INSERT INTO table VALUES(text,category)'.implode(','。$ sql));
toofarsideways

3
谢谢!固定。(我经常这样做...)
staticsan

1
并且查询实际上应该是'INSERT INTO table(text,category)VALUES'.implode(','。$ sql)'叹气4am编码导致可怕的调试:(
toofarsideways

3
我相信这段代码将为我的最新项目创建一个解决方案。我的问题是,这样做可以防止SQL注入吗?我的计划是转出mysql_real_escape_stringmysqli_real_escape_stringmysql_querymysqli_query,因为我用的MySQLi而这些都被弃用PHP5的。非常感谢!
wordman 2014年

2
mysql_*已从PHP中删除,因此请确保使用该mysqli_*接口。
瑞克·詹姆斯

60

现在,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
我认为这是进行多行插入而不是使用mysql_query的最推荐方法。因为当我们使用框架时,最好始终使用框架的内置功能。
Praneeth Nidarshan

22

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


6
我最近运行了一些基准测试,比较了此处提到的批量插入和准备好的插入语句。对于大约500次插入,准备好的插入方法在2.6-4.4秒之间完成,而批量插入方法在0.12-0.35秒之间完成。我本以为mysql会将这些准备好的语句“合并”在一起,并且性能与批量插入一样好,但是在默认设置下,性能差异显然很大。(顺便说一句,所有基准测试查询都在每个测试的单个事务中运行,以防止自动提交)
Motin 2011年

16

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

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

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

在插入许多行时关闭自动提交可以大大加快插入速度,因此请关闭它,然后按如上所述执行,或者仅创建一个字符串(sqlCombined),该字符串包含许多用分号和多查询分隔的插入语句,可以很好地处理它们。

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

[R


这是我使用您的想法得到的错误:“致命错误:在第30行的
/homepages/25/d402746174/htdocs/MoneyMachine/saveQuotes.php中的

8

您可以始终使用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值的笛卡尔积提供给我的,其中许多都是零。我需要使用中间表将其转换为多对多关系,以节省空间,因此需要插入而不是使用大容量插入
太远了,

您可以在处理完csv文件并调用加载数据的mysql语句后始终生成一个csv文件
Alexander Jardim 2013年

我认为知道该路径对于您的SQL客户端是本地的,而不是在SQL Server上是很有帮助的。该文件被上载到服务器,然后由它读取。我以为文件必须已经在服务器上,事实并非如此。如果它已在服务器上,请卸下该LOCAL位。
凯尔(Kyle)

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


1
如果将if移至查询上方并将其更改为if($ k> 0),则更加干净。
cherouvim 2010年

@cherouvim ...好吧,你是对的。感谢您的输入。在重新阅读我提供的示例时,我看不到您的观点。精心设计(通过pastebin等?)。谢谢
bdl 2010年

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

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

1

尽管现在回答这个问题为时已晚。这是我对同样的答案。

如果使用的是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的更多详细信息


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

0

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

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

0

我创建了一个简单的函数,你们可以轻松使用。您将需要针对插入的数据data array 传递table-name ($tbl)和table-field 。($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));
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.