chèn nhiều hàng thông qua một mảng php vào mysql


129

Tôi đang chuyển một tập dữ liệu lớn vào bảng MySQL thông qua PHP bằng cách sử dụng các lệnh chèn và tôi tự hỏi liệu có thể chèn khoảng 1000 hàng mỗi lần thông qua một truy vấn không ngoài việc nối từng giá trị vào cuối một chuỗi dài và sau đó thực hiện nó Tôi đang sử dụng khung CodeIgniter để các chức năng của nó cũng có sẵn cho tôi.


Tôi đã đưa ra câu trả lời theo câu hỏi của bạn cho phần chèn nhiều hàng của Codeigniter.
Somnath Muluk

@SomnathMuluk Cảm ơn bạn, tuy nhiên đã được một thời gian kể từ khi tôi cần trả lời câu hỏi này :) ...
toofarsideways

Tôi khuyên bạn nên sử dụng hàm insert_batch của CodeIgniter. Nếu bạn sử dụng thư viện, luôn cố gắng tận dụng điểm mạnh và tiêu chuẩn mã hóa của nó.
Dewald Els

Tôi tin rằng đợt chèn là cách tốt nhất để xem liên kết stackoverflow.com/questions/27206178/codeigniter-insert-batch
Syed Amir Bukhari

Câu trả lời:


234

Việc lắp ráp một INSERTcâu lệnh với nhiều hàng trong MySQL nhanh hơn nhiều so với một INSERTcâu lệnh trên mỗi hàng.

Điều đó nói rằng, có vẻ như bạn có thể đang gặp phải các vấn đề xử lý chuỗi trong PHP, đây thực sự là một vấn đề thuật toán, không phải là ngôn ngữ. Về cơ bản, khi làm việc với các chuỗi lớn, bạn muốn giảm thiểu việc sao chép không cần thiết. Chủ yếu, điều này có nghĩa là bạn muốn tránh nối. Cách nhanh nhất và hiệu quả nhất về bộ nhớ để xây dựng một chuỗi lớn, chẳng hạn như chèn hàng trăm hàng cùng một lúc, là tận dụng implode()chức năng và gán mảng.

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

Ưu điểm của phương pháp này là bạn không sao chép và sao chép lại câu lệnh SQL mà bạn đã tập hợp từ trước đến nay với mỗi cách ghép; thay vào đó, PHP thực hiện điều này một lần trong implode()câu lệnh. Đây là một chiến thắng lớn .

Nếu bạn có nhiều cột để đặt cùng nhau và một hoặc nhiều cột rất dài, bạn cũng có thể xây dựng một vòng lặp bên trong để làm điều tương tự và sử dụng implode()để gán mệnh đề giá trị cho mảng bên ngoài.


5
Cảm ơn vì điều đó! Btw thiếu một dấu ngoặc đóng ở cuối hàm nếu có ai định sao chép nó. mysql_real_query ('XÁC NHẬN VÀO GIÁ TRỊ bảng (văn bản, danh mục)' .implode (','. $ sql));
toofarsideways

3
Cảm ơn! Đã sửa. (Tôi thường làm điều đó ...)
staticsan

1
và truy vấn phải thực sự là 'Bảng xác nhận VÀO (văn bản, danh mục) GIÁ TRỊ' .implode (','. $ sql) 'thở dài mã hóa 4 giờ sáng dẫn đến gỡ lỗi khủng khiếp :(
toofarsideways

3
Tôi tin rằng mã này sẽ tạo ra một giải pháp cho dự án mới nhất của tôi. Câu hỏi của tôi ở đây là, điều này có an toàn khi tiêm SQL không? Kế hoạch của tôi là để chuyển đổi ra mysql_real_escape_stringvới mysqli_real_escape_stringmysql_queryvới mysqli_querynhư tôi đang sử dụng MySQLi và những đã bị phản đối như của PHP5. Cảm ơn nhiều!
wordman

2
mysql_*đã bị xóa khỏi PHP, vì vậy hãy chắc chắn sử dụng mysqli_*giao diện.
Rick James

60

Nhiều chèn / đợt chèn hiện được hỗ trợ bởi codeigniter. Tôi đã có vấn đề tương tự. Mặc dù đã rất muộn để trả lời câu hỏi, nhưng nó sẽ giúp được ai đó. Đó là lý do tại sao trả lời câu hỏi này.

$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
Tôi nghĩ rằng đây là cách được khuyến nghị nhất để thực hiện chèn nhiều hàng thay vì sử dụng mysql_query. Bởi vì khi chúng ta sử dụng một khung công tác, đó là một cách thực hành tốt để luôn sử dụng các tính năng tích hợp của khung.
Praneeth Nidarshan

22

Bạn có thể chuẩn bị truy vấn để chèn một hàng bằng lớp mysqli_stmt, sau đó lặp lại qua mảng dữ liệu. Cái gì đó như:

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

Trong đó 'idsb' là loại dữ liệu bạn liên kết (int, double, string, blob).


6
Gần đây tôi đã chạy một số điểm chuẩn so sánh các câu lệnh chèn số lượng lớn và chuẩn bị chèn như đã đề cập ở đây. Đối với khoảng 500 lần chèn, phương pháp chèn đã chuẩn bị hoàn thành trong khoảng 2,6-4,4 giây và phương pháp chèn số lượng lớn trong 0,12-0,35 giây. Tôi đã nghĩ rằng mysql sẽ "đóng gói" các câu lệnh được chuẩn bị này cùng nhau và thực hiện cũng như các phần chèn hàng loạt, nhưng trong một thiết lập mặc định, sự khác biệt hiệu năng rõ ràng là rất lớn. (Btw tất cả các truy vấn được điểm chuẩn đã chạy trong một giao dịch cho mỗi thử nghiệm, để ngăn chặn tự động cam kết)
Motin

16

Tôi biết đây là một truy vấn cũ, nhưng tôi chỉ đọc và nghĩ rằng tôi sẽ thêm những gì tôi tìm thấy ở nơi khác:

mysqli trong PHP 5 là một ojbect với một số chức năng tốt sẽ cho phép bạn tăng tốc thời gian chèn cho câu trả lời ở trên:

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

Tắt autocommit khi chèn nhiều hàng giúp tăng tốc độ chèn rất nhiều, vì vậy hãy tắt nó đi, sau đó thực hiện như đã đề cập ở trên hoặc chỉ tạo một chuỗi (sqlCombined), nhiều câu lệnh chèn được phân tách bằng dấu chấm phẩy và đa truy vấn sẽ xử lý chúng tốt.

Hy vọng điều này sẽ giúp ai đó tiết kiệm thời gian (tìm kiếm và chèn!)

R


Đây là lỗi tôi gặp phải khi sử dụng ý tưởng của bạn: "Lỗi nghiêm trọng: Gọi đến chức năng thành viên autocommit () trên null trong /homepages/25/d402746174/htdocs/MoneyMachine/saveQuotes.php trên dòng 30"
user3217883

8

Bạn luôn có thể sử dụng mysql LOAD DATA:

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

để thực hiện chèn số lượng lớn thay vì sử dụng một loạt các INSERTbáo cáo.


Tôi đã xem xét điều đó nhưng tôi cần phải thao tác dữ liệu trước khi chèn nó. Nó đã được trao cho tôi như là một sản phẩm của Cartesian với 1400 bộ 1400 giá trị int mà nhiều trong số đó là 0. Tôi cần chuyển đổi mối quan hệ đó thành nhiều mối quan hệ bằng cách sử dụng bảng trung gian để tiết kiệm dung lượng do đó cần phải chèn thay vì chèn số lượng lớn
toofarsideways

Bạn luôn có thể tạo tệp csv sau khi thao tác và gọi câu lệnh mysql tải dữ liệu
Alexander Jardim

Tôi nghĩ thật hữu ích khi biết rằng đường dẫn là cục bộ đến máy khách SQL của bạn chứ không phải trên máy chủ SQL. Các tập tin được tải lên máy chủ và sau đó đọc nó. Tôi nghĩ rằng các tập tin đã phải có trên máy chủ, đó không phải là trường hợp. Nếu nó đã có trên máy chủ, hãy loại bỏ LOCALbit.
Kyle

5

Chà, bạn không muốn thực hiện 1000 cuộc gọi truy vấn, nhưng thực hiện điều này là tốt:

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

Tùy thuộc vào nguồn dữ liệu của bạn, việc điền vào mảng có thể dễ dàng như mở tệp và đổ nội dung vào một mảng thông qua file().


1
Sẽ sạch hơn nếu bạn di chuyển nếu ở trên truy vấn và thay đổi nó thành một cái gì đó như if ($ k> 0).
cherouvim

@cherouvim ... Chà, bạn nói đúng về điều đó. Cảm ơn vì đầu vào của bạn. Khi tôi đọc lại ví dụ tôi đã cung cấp, tôi không thấy được quan điểm của bạn. Quan tâm đến công phu (thông qua pastebin, vv?). Cảm ơn-
bdl

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

Bạn có thể làm điều đó với một số cách trong codeigniter, vd

Đầu tiên theo vòng lặp

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

Thứ hai - Bằng cách chèn lô

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

Cách thứ ba - Bằng cách vượt qua nhiều giá trị

$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

Mặc dù đã quá muộn để trả lời câu hỏi này. Đây là câu trả lời của tôi trên cùng.

Nếu bạn đang sử dụng CodeIgniter thì bạn có thể sử dụng các phương thức inbuilt được định nghĩa trong lớp query_builder.

$ this-> db-> insert_batch ()

Tạo chuỗi chèn dựa trên dữ liệu bạn cung cấp và chạy truy vấn. Bạn có thể truyền một mảng hoặc một đối tượng cho hàm. Dưới đây là một ví dụ sử dụng một mảng:

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

Tham số đầu tiên sẽ chứa tên bảng, thứ hai là một mảng các giá trị kết hợp.

Bạn có thể tìm thêm chi tiết về query_builder tại đây


0

Tôi đã tạo một lớp thực hiện nhiều dòng được sử dụng như sau:

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

trong đó lớp được định nghĩa như sau:

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

Sử dụng lô chèn trong codeigniter để chèn nhiều hàng dữ liệu.

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

0

Tôi đã tạo ra chức năng đơn giản này mà các bạn có thể sử dụng dễ dàng. Bạn sẽ cần phải chuyển tên ($tbl)bảng, trường bảng ($insertFieldsArr)đối với dữ liệu chèn, mảng dữ liệu của bạn ($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));
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.