แทรกหลายแถวผ่านอาร์เรย์ php ลงใน mysql


129

ฉันส่งชุดข้อมูลขนาดใหญ่ลงในตาราง MySQL ผ่าน PHP โดยใช้คำสั่งแทรกและฉันสงสัยว่าเป็นไปได้หรือไม่ที่จะแทรกประมาณ 1,000 แถวต่อครั้งผ่านการสืบค้นนอกเหนือจากการเติมแต่ละค่าในตอนท้ายของสตริงที่มีความยาวหนึ่งไมล์ รันมัน ฉันกำลังใช้กรอบงาน CodeIgniter เพื่อให้สามารถใช้งานฟังก์ชันได้เช่นกัน


ฉันได้รับคำตอบตามคำถามของคุณสำหรับการแทรกหลายแถวของ Codeigniter
Somnath Muluk

@SomnathMuluk ขอบคุณ แต่มันเป็นเวลานานแล้วที่ฉันจำเป็นต้องตอบคำถามนี้
:)

ฉันอยากจะแนะนำให้ใช้ฟังก์ชั่น insert_batch ของ CodeIgniter หากคุณใช้ห้องสมุดพยายามใช้ประโยชน์จากความแข็งแกร่งและมาตรฐานการเข้ารหัสเสมอ
Dewald Els

ฉันเชื่อว่าการแทรกแบทช์เป็นวิธีที่ดีที่สุดในการทำเช่นนั้นดูลิงก์stackoverflow.com/questions/27206178/codeigniter-insert-batch
Syed Amir Bukhari

คำตอบ:


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()เพื่อกำหนดค่าส่วนคำสั่งให้กับอาร์เรย์ด้านนอก


5
ขอบคุณสำหรับสิ่งนั้น! Btw คุณหายไปวงเล็บปิดในตอนท้ายของฟังก์ชั่นถ้าใครวางแผนที่จะคัดลอกมัน mysql_real_query ('INSERT INTO VALUES ตาราง (ข้อความ, หมวดหมู่)' .implode (','. $ sql));
toofarsideways

3
ขอบคุณ! แก้ไขแล้ว. (ผมมักจะทำอย่างนั้น ... )
staticsan

1
และข้อความค้นหาควรเป็น 'INSERT INTO table (ข้อความ, หมวดหมู่) ค่า' .implode (','. $ sql) 'ถอนหายใจ 4am การเข้ารหัสจะนำไปสู่การดีบั๊กอันน่ากลัว :(
toofarsideways

3
ฉันเชื่อว่ารหัสนี้จะสร้างโซลูชันสำหรับโครงการล่าสุดของฉัน คำถามของฉันอยู่ที่นี่ปลอดภัยจากการฉีด SQL หรือไม่ แผนของฉันเพื่อสลับออกมาmysql_real_escape_stringด้วยmysqli_real_escape_stringและmysql_queryด้วยความmysqli_queryที่ฉันใช้ MySQLi และเหล่านี้ได้รับการคัดค้าน ณ PHP5 ขอบคุณมาก!
Wordman

2
mysql_*ถูกลบออกจาก PHP ดังนั้นโปรดใช้mysqli_*อินเตอร์เฟส
Rick James

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 จะ "รวมกลุ่ม" ข้อความที่เตรียมไว้เหล่านี้เข้าด้วยกันและดำเนินการเช่นเดียวกับแทรกจำนวนมาก แต่ในการตั้งค่าเริ่มต้นความแตกต่างด้านประสิทธิภาพนั้นชัดเจนมาก (Btw แบบสอบถามที่มีการเปรียบเทียบทั้งหมดนั้นทำงานอยู่ในการทำธุรกรรมครั้งเดียวสำหรับการทดสอบแต่ละครั้งเพื่อป้องกันการตอบรับอัตโนมัติ)
Motin

16

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ฉันเพิ่งอ่านและคิดว่าฉันจะเพิ่มสิ่งที่ฉันพบที่อื่น:

mysqli ใน PHP 5 เป็นฟังก์ชันที่มีฟังก์ชั่นที่ดีซึ่งจะช่วยให้คุณเพิ่มเวลาในการแทรกคำตอบด้านบน:

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

การปิดการเติมข้อความอัตโนมัติเมื่อแทรกหลาย ๆ แถวอย่างรวดเร็วเพิ่มการแทรกดังนั้นปิดแล้วดำเนินการตามที่กล่าวไว้ข้างต้นหรือเพียงแค่สร้างสตริง (sqlCombined) ซึ่งเป็นคำสั่งแทรกจำนวนมากคั่นด้วยเซมิโคลอน

หวังว่านี่จะช่วยให้ใครบางคนประหยัดเวลา (การค้นหาและการแทรก!)

R


นี่เป็นข้อผิดพลาดที่ฉันได้ใช้ความคิดของคุณ: "ข้อผิดพลาดร้ายแรง: การเรียกไปยังฟังก์ชันสมาชิก autocommit () บน null ใน /homepages/25/d402746174/htdocs/MoneyMachine/saveQuotes.php ที่บรรทัด 30"
user3217883

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งบจำนวนมาก


ฉันได้ตรวจสอบแล้ว แต่ฉันต้องจัดการข้อมูลก่อนที่จะใส่เข้าไป มันมอบให้ฉันในฐานะผลิตภัณฑ์คาร์ทีเซียนที่มีค่า int 1400 คูณ 1,400 ค่าซึ่งส่วนใหญ่เป็นศูนย์ ฉันต้องแปลงมันให้มีความสัมพันธ์แบบหลายต่อหลายอย่างโดยใช้ตารางตัวกลางเพื่อประหยัดพื้นที่ดังนั้นความต้องการการแทรกเมื่อเทียบกับการแทรกจำนวนมาก
toofarsideways

คุณสามารถสร้างไฟล์ csv ได้หลังจากจัดการมันแล้วเรียกคำสั่ง mysql ที่โหลดข้อมูล
Alexander Jardim

ฉันคิดว่ามันมีประโยชน์ที่จะทราบว่าเส้นทางนั้นอยู่ภายในเครื่องกับไคลเอนต์ SQL ของคุณไม่ใช่ในเซิร์ฟเวอร์ SQL ไฟล์ถูกอัปโหลดไปยังเซิร์ฟเวอร์แล้วอ่านโดยมัน ฉันคิดว่าไฟล์ต้องอยู่บนเซิร์ฟเวอร์อยู่แล้วซึ่งไม่ใช่ในกรณีนี้ ถ้าอยู่บนเซิร์ฟเวอร์แล้วให้ลบLOCALบิต
Kyle

5

คุณไม่ต้องการเรียกการสอบถาม 1,000 ครั้ง แต่การทำเช่นนี้ถือว่าใช้ได้:

$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
มันสะอาดกว่าถ้าคุณย้ายที่ถ้าเหนือแบบสอบถามและเปลี่ยนเป็นอะไรเช่นถ้า ($ k> 0)
cherouvim

@cherouvim ... เอาละเจ้าพูดถูก ขอบคุณสำหรับข้อมูลของคุณ ในขณะที่ฉันกำลังอ่านตัวอย่างที่ฉันให้อีกครั้งฉันไม่เห็นจุดของคุณ สนใจที่จะทำอย่างละเอียด (ผ่าน pastebin ฯลฯ ?) Thanks-
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

คุณสามารถทำได้หลายวิธีใน 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 คุณสามารถใช้วิธีการ inbuilt ที่กำหนดไว้ในคลาส 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 ถูกกำหนดดังนี้:

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 เพื่อแทรกข้อมูลหลายแถว

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

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));
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.