Chuẩn bị PDO Chèn nhiều hàng trong một truy vấn


145

Tôi hiện đang sử dụng loại SQL này trên MySQL để chèn nhiều hàng giá trị trong một truy vấn duy nhất:

INSERT INTO `tbl` (`key1`,`key2`) VALUES ('r1v1','r1v2'),('r2v1','r2v2'),...

Trong các bài đọc về PDO, các câu lệnh được chuẩn bị sử dụng sẽ cho tôi sự bảo mật tốt hơn các truy vấn tĩnh.

Do đó, tôi muốn biết liệu có thể tạo "chèn nhiều hàng giá trị bằng cách sử dụng một truy vấn" bằng cách sử dụng các câu lệnh đã chuẩn bị hay không.

Nếu có, tôi có thể biết làm thế nào tôi có thể thực hiện nó?


cẩn thận với rất nhiều câu trả lời cho $stmt->execute($data); php.net/manual/en/ Nhật Về cơ bản tất cả các thông số đều được thông qua dưới dạng chuỗi. Chỉ cần lặp qua dữ liệu sau khi xây dựng truy vấn và nhập thủ công bindValuehoặc bindParamchuyển loại làm đối số thứ ba.
MrMesees

Câu trả lời:


150

Chèn nhiều giá trị với các tuyên bố chuẩn bị PDO

Chèn nhiều giá trị trong một câu lệnh thực thi. Tại sao bởi vì theo trang này, nó nhanh hơn so với chèn thông thường.

$datafields = array('fielda', 'fieldb', ... );

$data[] = array('fielda' => 'value', 'fieldb' => 'value' ....);
$data[] = array('fielda' => 'value', 'fieldb' => 'value' ....);

nhiều giá trị dữ liệu hơn hoặc bạn có thể có một vòng lặp điền dữ liệu.

Với các phần chèn được chuẩn bị, bạn cần biết các trường bạn đang chèn và số lượng các trường để tạo? giữ chỗ để ràng buộc các tham số của bạn.

insert into table (fielda, fieldb, ... ) values (?,?...), (?,?...)....

Về cơ bản, đó là cách chúng ta muốn câu lệnh chèn trông như thế nào.

Bây giờ, mã:

function placeholders($text, $count=0, $separator=","){
    $result = array();
    if($count > 0){
        for($x=0; $x<$count; $x++){
            $result[] = $text;
        }
    }

    return implode($separator, $result);
}

$pdo->beginTransaction(); // also helps speed up your inserts.
$insert_values = array();
foreach($data as $d){
    $question_marks[] = '('  . placeholders('?', sizeof($d)) . ')';
    $insert_values = array_merge($insert_values, array_values($d));
}

$sql = "INSERT INTO table (" . implode(",", $datafields ) . ") VALUES " .
       implode(',', $question_marks);

$stmt = $pdo->prepare ($sql);
try {
    $stmt->execute($insert_values);
} catch (PDOException $e){
    echo $e->getMessage();
}
$pdo->commit();

Mặc dù trong thử nghiệm của tôi, chỉ có chênh lệch 1 giây khi sử dụng nhiều lần chèn và các lần chèn được chuẩn bị thường xuyên với một giá trị duy nhất.


4
Một lỗi đánh máy, trong phần giải thích ở trên, nó đề cập đến $ datafields mặc dù $ datafield được sử dụng trong $ sql. Do đó, sao chép dán sẽ dẫn đến lỗi. Xin hãy khắc phục. Cảm ơn cho giải pháp này mặc dù.
pal4life

1
Đã sử dụng điều này trong một thời gian sau đó nhận thấy rằng các giá trị có dấu ngoặc đơn trong chúng không được thoát đúng. Sử dụng dấu ngoặc kép về sự bùng nổ hoạt động giống như một cơ duyên đối với tôi: $ a [] = '("'. Imode (", ", $ question_mark). '", NOW ())';
qwertzman

1
Array_merge có vẻ đắt hơn so với việc chỉ sử dụng một mảng_push.
K2xL

14
Khi bạn nói "chỉ có chênh lệch 1 giây", bạn đã chèn bao nhiêu hàng dữ liệu? 1 giây là khá quan trọng tùy thuộc vào bối cảnh.
Kevin Dice

3
Tối ưu hóa: Không có điểm trong việc gọi đi gọi placeholders()lại. Gọi nó một lần trước vòng lặp với sizeof($datafields)và nối chuỗi kết quả vào $question_marks[]bên trong vòng lặp.
Nhà phát triển AVID

71

Câu trả lời tương tự như ông Balagtas, hơi rõ ràng hơn ...

Các phiên bản gần đây MySQL và PHP PDO không hỗ trợ các INSERTcâu lệnh nhiều hàng .

Tổng quan về SQL

SQL sẽ trông giống như thế này, giả sử một bảng 3 cột mà bạn muốn INSERT.

INSERT INTO tbl_name
            (colA, colB, colC)
     VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?) [,...]

ON DUPLICATE KEY UPDATEhoạt động như mong đợi ngay cả với một INSERT nhiều hàng; nối thêm này:

ON DUPLICATE KEY UPDATE colA = VALUES(colA), colB = VALUES(colB), colC = VALUES(colC)

Tổng quan về PHP

Mã PHP của bạn sẽ làm theo như thường lệ $pdo->prepare($qry)$stmt->execute($params)các cuộc gọi PDO.

$paramssẽ là mảng 1 chiều của tất cả các giá trị được truyền cho INSERT.

Trong ví dụ trên, nó nên chứa 9 phần tử; PDO sẽ sử dụng mỗi bộ 3 dưới dạng một hàng giá trị. (Chèn 3 hàng 3 cột mỗi cột = 9 mảng phần tử.)

Thực hiện

Mã dưới đây được viết cho rõ ràng, không hiệu quả. Làm việc với các array_*()hàm PHP để biết cách tốt hơn để ánh xạ hoặc duyệt qua dữ liệu của bạn nếu bạn muốn. Việc bạn có thể sử dụng các giao dịch rõ ràng hay không phụ thuộc vào loại bảng MySQL của bạn.

Giả định:

  • $tblName - tên chuỗi của bảng để XÁC NHẬN
  • $colNames- Mảng 1 chiều của các tên cột của bảng Các tên cột này phải là định danh cột MySQL hợp lệ; thoát chúng bằng backticks (``) nếu chúng không
  • $dataVals - mảng đa chiều, trong đó mỗi phần tử là mảng 1-d của một hàng các giá trị cho INSERT

Mã mẫu

// setup data values for PDO
// memory warning: this is creating a copy all of $dataVals
$dataToInsert = array();

foreach ($dataVals as $row => $data) {
    foreach($data as $val) {
        $dataToInsert[] = $val;
    }
}

// (optional) setup the ON DUPLICATE column names
$updateCols = array();

foreach ($colNames as $curCol) {
    $updateCols[] = $curCol . " = VALUES($curCol)";
}

$onDup = implode(', ', $updateCols);

// setup the placeholders - a fancy way to make the long "(?, ?, ?)..." string
$rowPlaces = '(' . implode(', ', array_fill(0, count($colNames), '?')) . ')';
$allPlaces = implode(', ', array_fill(0, count($dataVals), $rowPlaces));

$sql = "INSERT INTO $tblName (" . implode(', ', $colNames) . 
    ") VALUES " . $allPlaces . " ON DUPLICATE KEY UPDATE $onDup";

// and then the PHP PDO boilerplate
$stmt = $pdo->prepare ($sql);

try {
   $stmt->execute($dataToInsert);
} catch (PDOException $e){
   echo $e->getMessage();
}

$pdo->commit();

6
Điều đó thực sự quá tệ khi PDO xử lý theo cách này, có một số cách rất thanh lịch để làm điều này trong các trình điều khiển DB khác.
Jonathon

Điều này thiết lập các trình giữ chỗ thậm chí còn căng thẳng hơn, $rowPlaceskhông còn cần thiết:$allPlaces = implode(',', array_fill(0, count($dataVals), '('.str_pad('', (count($colNames)*2)-1, '?,').')'));
Phil

Hoạt động hoàn hảo. Tôi sẽ thêm vào câu trả lời này sự cần thiết để đảm bảo tính duy nhất của các chỉ mục (kết hợp) trong bảng. Giống như trong ALTER TABLE votesADD UNIQUE unique_index( user, email, address);
Giuseppe

1
Tuyệt vời! BTW, sử dụng array_push($dataToInsert, ...array_values($dataVals));sẽ nhanh hơn nhiều sau đóforeach ($dataVals as $row => $data) {}
Anis

39

Đối với những gì đáng giá, tôi đã thấy rất nhiều người dùng khuyên bạn nên lặp qua các câu lệnh INSERT thay vì xây dựng như một truy vấn chuỗi đơn như câu trả lời đã chọn. Tôi quyết định chạy thử nghiệm đơn giản chỉ với hai trường và câu lệnh chèn rất cơ bản:

<?php
require('conn.php');

$fname = 'J';
$lname = 'M';

$time_start = microtime(true);
$stmt = $db->prepare('INSERT INTO table (FirstName, LastName) VALUES (:fname, :lname)');

for($i = 1; $i <= 10; $i++ )  {
    $stmt->bindParam(':fname', $fname);
    $stmt->bindParam(':lname', $lname);
    $stmt->execute();

    $fname .= 'O';
    $lname .= 'A';
}


$time_end = microtime(true);
$time = $time_end - $time_start;

echo "Completed in ". $time ." seconds <hr>";

$fname2 = 'J';
$lname2 = 'M';

$time_start2 = microtime(true);
$qry = 'INSERT INTO table (FirstName, LastName) VALUES ';
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?), ";
$qry .= "(?,?)";

$stmt2 = $db->prepare($qry);
$values = array();

for($j = 1; $j<=10; $j++) {
    $values2 = array($fname2, $lname2);
    $values = array_merge($values,$values2);

    $fname2 .= 'O';
    $lname2 .= 'A';
}

$stmt2->execute($values);

$time_end2 = microtime(true);
$time2 = $time_end2 - $time_start2;

echo "Completed in ". $time2 ." seconds <hr>";
?>

Mặc dù bản thân truy vấn tổng thể mất một phần nghìn giây hoặc ít hơn, truy vấn sau (chuỗi đơn) luôn nhanh hơn 8 lần hoặc hơn. Nếu điều này được xây dựng để nói phản ánh việc nhập hàng ngàn hàng trên nhiều cột khác nhau, sự khác biệt có thể là rất lớn.


@ JM4 - ý tưởng tuyệt vời để đặt 10 hàng trực tiếp trong một lần thực hiện . Nhưng làm thế nào tôi có thể chèn hàng ngàn hàng khi chúng được lưu trữ trong một đối tượng như JSON? Mã của tôi dưới đây hoạt động perferctly. Nhưng làm thế nào tôi có thể điều chỉnh nó để chèn 10 hàng trong một lần thực hiện? `foreach ($ json_content as $ datarow) {$ id = $ datarow [id]; $ date = $ datarow [ngày]; $ row3 = $ datarow [row3]; $ row4 = $ datarow [row4]; $ row5 = $ datarow [row5]; $ row6 = $ datarow [row6]; $ row7 = $ datarow [row7]; // bây giờ thực thi $ databaseinsert-> exec (); } // kết thúc cuộc thảo luận `
Peter

@ JM4 - ... và câu hỏi thứ hai của tôi là: "tại sao không có bind_paramtuyên bố nào trong quy trình nhập khẩu thứ hai"?
Peter

Bạn sẽ không phải lặp lại hai lần chứ? Bạn cũng sẽ phải tự động tạo ra (?,?), phải không?
NoobishPro

@NoobishPro Có, bạn có thể sử dụng cùng một / foreach để tạo cả hai.
Chazy Chaz

34

Câu trả lời được chấp nhận của Herbert Balagtas hoạt động tốt khi mảng dữ liệu $ nhỏ. Với mảng dữ liệu $ lớn hơn, hàm Array_merge trở nên cực kỳ chậm. Tệp thử nghiệm của tôi để tạo mảng dữ liệu $ có 28 cols và khoảng 80.000 dòng. Kịch bản cuối cùng mất 41 giây để hoàn thành.

Sử dụng mảng_push () để tạo $ insert_values ​​thay vì Array_merge () dẫn đến tăng tốc độ 100 lần với thời gian thực hiện là 0,41 giây .

Mảng có vấn đề_merge ():

$insert_values = array();

foreach($data as $d){
 $question_marks[] = '('  . placeholders('?', sizeof($d)) . ')';
 $insert_values = array_merge($insert_values, array_values($d));
}

Để loại bỏ sự cần thiết của mảng_merge (), bạn có thể xây dựng hai mảng sau:

//Note that these fields are empty, but the field count should match the fields in $datafields.
$data[] = array('','','','',... n ); 

//getting rid of array_merge()
array_push($insert_values, $value1, $value2, $value3 ... n ); 

Những mảng này sau đó có thể được sử dụng như sau:

function placeholders($text, $count=0, $separator=","){
    $result = array();
    if($count > 0){
        for($x=0; $x<$count; $x++){
            $result[] = $text;
        }
    }

    return implode($separator, $result);
}

$pdo->beginTransaction();

foreach($data as $d){
 $question_marks[] = '('  . placeholders('?', sizeof($d)) . ')';
}

$sql = "INSERT INTO table (" . implode(",", array_keys($datafield) ) . ") VALUES " . implode(',', $question_marks);

$stmt = $pdo->prepare ($sql);
try {
    $stmt->execute($insert_values);
} catch (PDOException $e){
    echo $e->getMessage();
}
$pdo->commit();

4
Trong PHP 5.6 bạn có thể làm array_push($data, ...array_values($row))thay vì $data = array_merge($data, array_values($row));. Nhanh hơn nhiều.
mở

Tại sao 5,6? Tài liệu không nói gì về 5.6, array_push()có sẵn ngay cả trong php 4.
ZurabWeb

1
@Piero đó là mã PHP 5.6+ không phải vì sử dụng array_push()mà vì @Mark đang sử dụng giải nén đối số. Thông báo ...array_values()cuộc gọi ở đó?
mariano.iglesias

@ mariano.iglesias array_values()cũng có sẵn trong php 4. Không chắc đó có phải ý bạn không argument unpacking.
ZurabWeb

2
@Piero, Giải nén đối số là một tính năng được giới thiệu trong PHP 5.6. Đó là một cách để cung cấp nhiều đối số dưới dạng một mảng. Kiểm tra tại đây - php.net/manual/en/ Kẻ
Anis

14

Hai cách tiếp cận có thể:

$stmt = $pdo->prepare('INSERT INTO foo VALUES(:v1_1, :v1_2, :v1_3),
    (:v2_1, :v2_2, :v2_3),
    (:v2_1, :v2_2, :v2_3)');
$stmt->bindValue(':v1_1', $data[0][0]);
$stmt->bindValue(':v1_2', $data[0][1]);
$stmt->bindValue(':v1_3', $data[0][2]);
// etc...
$stmt->execute();

Hoặc là:

$stmt = $pdo->prepare('INSERT INTO foo VALUES(:a, :b, :c)');
foreach($data as $item)
{
    $stmt->bindValue(':a', $item[0]);
    $stmt->bindValue(':b', $item[1]);
    $stmt->bindValue(':c', $item[2]);
    $stmt->execute();
}

Nếu dữ liệu cho tất cả các hàng nằm trong một mảng, tôi sẽ sử dụng giải pháp thứ hai.


10
sau này bạn không thực hiện một số (có thể hàng ngàn) cuộc gọi thực thi riêng thay vì kết hợp thành một câu lệnh?
JM4

@ JM4, bạn có gợi ý $stmt->execute();nên ở ngoài vòng foreach không?
bafromca

@bafromca - Vâng tôi đây. Xem câu trả lời của tôi ở trên với upvotes. Trên một câu lệnh chèn thuần túy, không có lý do gì tôi có thể đưa ra một cách hợp lý rằng nó không thể là một câu lệnh đơn. Một cuộc gọi, một thực hiện. Trên thực tế, câu trả lời của tôi từ đầu năm 2012 có thể được cải thiện hơn nữa - điều mà tôi sẽ làm sau này khi có thêm thời gian. Nếu bạn bắt đầu ném vào các kết hợp Chèn / cập nhật / xóa, đó là một câu chuyện khác.
JM4

12

Đó đơn giản không phải là cách bạn sử dụng các tuyên bố đã chuẩn bị.

Hoàn toàn ổn khi chèn một hàng cho mỗi truy vấn vì bạn có thể thực thi một câu lệnh được chuẩn bị nhiều lần với các tham số khác nhau. Trong thực tế đó là một trong những lợi thế lớn nhất vì nó cho phép bạn chèn cho bạn một số lượng lớn các hàng một cách hiệu quả, an toàn và thoải mái.

Vì vậy, có thể thực hiện lược đồ mà bạn đề xuất, ít nhất là cho một số hàng cố định, nhưng gần như đảm bảo rằng đây không thực sự là những gì bạn muốn.


1
Bạn có thể đề xuất một cách tốt hơn để chèn nhiều hàng vào một bảng không?
Crashthatch

@Crashthatch: Chỉ cần thực hiện theo cách ngây thơ: Thiết lập câu lệnh đã chuẩn bị một lần, sau đó thực hiện nó cho mỗi hàng với các giá trị khác nhau cho các tham số bị ràng buộc. Đó là cách tiếp cận thứ hai trong câu trả lời của Zyk.
sebasgo

2
Mục đích bạn đề cập cho tuyên bố chuẩn bị là đúng. Nhưng, sử dụng multi -insert là một kỹ thuật khác để cải thiện tốc độ chèn và nó cũng có thể được sử dụng với câu lệnh được chuẩn bị. Theo kinh nghiệm của tôi, trong khi di chuyển 30 triệu dữ liệu hàng bằng cách sử dụng câu lệnh được chuẩn bị PDO, tôi thấy đa chèn nhanh hơn 7-10 lần sau đó được nhóm một lần chèn trong các giao dịch.
Anis

1
Hoàn toàn đồng ý với Anis. Tôi có 100 nghìn hàng và được tăng tốc độ rất lớn với các hàng chèn khác nhau.
Kenneth

Khẳng định rằng việc gọi một cơ sở dữ liệu quan hệ trong một vòng lặp một lần trên mỗi hàng nói chung là một điều tốt là điều tôi không thể đồng ý. Downvote cho điều đó. Cấp, đôi khi nó là ok. Tôi không tin vào sự tuyệt đối với kỹ thuật. Nhưng đây là một mô hình chống chỉ nên được sử dụng trong các trường hợp chọn lọc.
Brandon

8

Một câu trả lời ngắn hơn: làm phẳng mảng dữ liệu theo thứ tự các cột sau đó

//$array = array( '1','2','3','4','5', '1','2','3','4','5');
$arCount = count($array);
$rCount = ($arCount  ? $arCount - 1 : 0);
$criteria = sprintf("(?,?,?,?,?)%s", str_repeat(",(?,?,?,?,?)", $rCount));
$sql = "INSERT INTO table(c1,c2,c3,c4,c5) VALUES$criteria";

Khi chèn 1.000 bản ghi hoặc hơn, bạn không muốn phải lặp qua mọi bản ghi để chèn chúng khi tất cả những gì bạn cần là số lượng giá trị.


5

Đây là cách tiếp cận đơn giản của tôi.

    $values = array();
    foreach($workouts_id as $value){
      $_value = "(".$value.",".$plan_id.")";
      array_push($values,$_value);
    }
    $values_ = implode(",",$values);

    $sql = "INSERT INTO plan_days(id,name) VALUES" . $values_."";
    $stmt = $this->conn->prepare($sql);
    $stmt->execute();

6
bạn đang đánh bại quan điểm sử dụng các tuyên bố đã chuẩn bị. op quan tâm đến vấn đề bảo mật trong câu hỏiOn the readings on PDO, the use prepared statements should give me a better security than static queries.
YesItsMe

2
Chỉ cần hình ảnh mà bạn đã không xác nhận $workouts_id, có thể có $values với dữ liệu khá bất ngờ. Bạn không thể đảm bảo rằng có thể không phải bây giờ nhưng trong tương lai một nhà phát triển khác làm cho dữ liệu này không an toàn. Vì vậy, tôi nghĩ khá đúng khi thực hiện truy vấn được chuẩn bị bởi PDO.
Nikita_kharkov_ua

3

Đây là một lớp tôi đã viết thực hiện nhiều lần chèn với tùy chọn thanh lọc:

<?php

/**
 * $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;

    function __construct(\PDO $pdo, $tableName, $fieldsAsArray, $bigInsertCount = 100) {
        $this->_numberOfFields = count($fieldsAsArray);
        $insertIntoPortion = "INSERT 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;
    }
}

Xin chào Pierre. Có lẽ bạn không hoạt động ở đây nữa. Tuy nhiên, tôi chỉ muốn chỉ ra rằng ý tưởng của tôi cho vấn đề này trông gần giống với bạn. Sự trùng hợp thuần túy, như tôi đoán không còn nhiều điều này nữa. Tôi cũng đã thêm các lớp cho DELETE- VÀ CẬP NHẬT-Hoạt động và liên quan đến một số ý tưởng từ đây, sau đó. Tôi chỉ không thấy lớp của bạn. Xin thứ lỗi cho sự tự quảng cáo không biết xấu hổ của tôi ở đây, nhưng tôi đoán nó sẽ giúp ích cho ai đó. Hy vọng điều này không chống lại SO-Rules. Tìm nó ở đây .
JackLeEmmerdeur

1

Đây là cách tôi đã làm nó:

Trước tiên, hãy xác định tên cột bạn sẽ sử dụng hoặc để trống và pdo sẽ cho rằng bạn muốn sử dụng tất cả các cột trên bảng - trong trường hợp đó bạn sẽ cần thông báo các giá trị hàng theo thứ tự chính xác chúng xuất hiện trên bảng .

$cols = 'name', 'middleName', 'eMail';
$table = 'people';

Bây giờ, giả sử bạn đã chuẩn bị một mảng hai chiều. Lặp lại nó và xây dựng một chuỗi với các giá trị hàng của bạn, như sau:

foreach ( $people as $person ) {
if(! $rowVals ) {
$rows = '(' . "'$name'" . ',' . "'$middleName'" . ',' .           "'$eMail'" . ')';
} else { $rowVals  = '(' . "'$name'" . ',' . "'$middleName'" . ',' . "'$eMail'" . ')';
}

Bây giờ, những gì bạn vừa làm là kiểm tra xem $ rows đã được xác định chưa, và nếu không, hãy tạo nó và lưu trữ các giá trị hàng và cú pháp SQL cần thiết để nó sẽ là một câu lệnh hợp lệ. Lưu ý rằng các chuỗi nên đi vào bên trong dấu ngoặc kép và dấu ngoặc đơn, vì vậy chúng sẽ được nhận ra ngay lập tức như vậy.

Tất cả những gì còn lại phải làm là chuẩn bị tuyên bố và thực hiện, như sau:

$stmt = $db->prepare ( "INSERT INTO $table $cols VALUES $rowVals" );
$stmt->execute ();

Đã thử nghiệm với tối đa 2000 hàng cho đến nay và thời gian thực hiện là ảm đạm. Sẽ chạy thêm một số bài kiểm tra và sẽ quay lại đây trong trường hợp tôi có thêm điều gì đó để đóng góp.

Trân trọng.


1

Vì chưa được đề xuất, tôi khá chắc chắn LOAD DATA INFILE vẫn là cách nhanh nhất để tải dữ liệu vì nó vô hiệu hóa lập chỉ mục, chèn tất cả dữ liệu và sau đó bật lại các chỉ mục - tất cả trong một yêu cầu.

Lưu dữ liệu dưới dạng csv nên khá đơn giản trong tâm trí fputcsv. MyISAM là nhanh nhất, nhưng bạn vẫn có được hiệu suất lớn trong InnoDB. Tuy nhiên, có những nhược điểm khác, vì vậy tôi sẽ đi theo con đường này nếu bạn đang chèn nhiều dữ liệu và không bận tâm với dưới 100 hàng.


1

Mặc dù một câu hỏi cũ, tất cả các đóng góp đã giúp tôi rất nhiều vì vậy đây là giải pháp của tôi, hoạt động trong DbContextlớp học của riêng tôi . Các $rowstham số chỉ đơn giản là một mảng của các mảng kết hợp đại diện cho hàng hoặc các mô hình: field name => insert value.

Nếu bạn sử dụng một mẫu sử dụng các mô hình, điều này sẽ phù hợp một cách độc đáo khi truyền dữ liệu mô hình dưới dạng một mảng, hãy nói từ một ToRowArrayphương thức trong lớp mô hình.

Lưu ý : Không nên nói nhưng không bao giờ cho phép các đối số được truyền cho phương thức này được hiển thị cho người dùng hoặc phụ thuộc vào bất kỳ đầu vào nào của người dùng, ngoại trừ các giá trị chèn đã được xác thực và khử trùng. Đối $tableNamesố và tên cột phải được xác định bởi logic gọi; ví dụ, một Usermô hình có thể được ánh xạ tới bảng người dùng, trong đó danh sách cột của nó được ánh xạ tới các trường thành viên của mô hình.

public function InsertRange($tableName, $rows)
{
    // Get column list
    $columnList = array_keys($rows[0]);
    $numColumns = count($columnList);
    $columnListString = implode(",", $columnList);

    // Generate pdo param placeholders
    $placeHolders = array();

    foreach($rows as $row)
    {
        $temp = array();

        for($i = 0; $i < count($row); $i++)
            $temp[] = "?";

        $placeHolders[] = "(" . implode(",", $temp) . ")";
    }

    $placeHolders = implode(",", $placeHolders);

    // Construct the query
    $sql = "insert into $tableName ($columnListString) values $placeHolders";
    $stmt = $this->pdo->prepare($sql);

    $j = 1;
    foreach($rows as $row)
    {
        for($i = 0; $i < $numColumns; $i++)
        {
            $stmt->bindParam($j, $row[$columnList[$i]]);
            $j++;
        }
    }

    $stmt->execute();
}

thoát khỏi một giao dịch, vì sẽ vô nghĩa khi sử dụng một giao dịch cho một truy vấn duy nhất. và như thường lệ, mã này dễ bị lỗi SQL hoặc lỗi truy vấn.
Ý thức chung của bạn

Bạn nói đúng về việc sử dụng các giao dịch dư thừa trong trường hợp này, nhưng tôi không thấy điều này dễ bị tổn thương khi tiêm SQL. Nó được tham số hóa nên tôi chỉ có thể đoán rằng bạn giả sử $tableNameđược tiếp xúc với người dùng, điều đó không phải, đó là trong DAL. Bạn có thể mở rộng về yêu cầu của bạn? Nó không hữu ích để chỉ nói những điều.
Lee

tốt, đó không chỉ là tên bảng mà còn: làm thế nào bạn có thể biết liệu nó có bị lộ hay không bởi bất kỳ ai sẽ sử dụng mã bạn đã đăng ở đây?
Ý thức chung của bạn

Vì vậy, trách nhiệm của người đăng là phác thảo mọi cách sử dụng mã tiềm năng hoặc mọi nguồn để tranh luận? Có lẽ tôi có kỳ vọng cao hơn của mọi người. Nó sẽ làm cho bạn hạnh phúc hơn nếu tôi thêm một lưu ý không cho phép người dùng có quyền truy cập $tableName?
Lee

Trách nhiệm của người đăng là gửi một mã đáng tin cậy, nếu mục đích của họ là giúp đỡ ai đó, không chỉ để thể hiện.
Ý thức chung của bạn

1

Đây là một giải pháp (mỏng) khác cho vấn đề này:

Đầu tiên, bạn cần đếm dữ liệu của mảng nguồn (ở đây: $ aData) với Count (). Sau đó, bạn sử dụng mảng_fill () và tạo một mảng mới có nhiều mục như mảng nguồn có, mỗi mục có giá trị "(?,?)" (Số lượng giữ chỗ phụ thuộc vào các trường bạn sử dụng; ở đây: 2). Sau đó, mảng được tạo cần phải được mã hóa và như một dấu phẩy được sử dụng. Trong vòng lặp foreach, bạn cần tạo một chỉ mục khác về số lượng giữ chỗ bạn sử dụng (số lượng giữ chỗ * chỉ số mảng hiện tại + 1). Bạn cần thêm 1 vào chỉ mục được tạo sau mỗi giá trị được liên kết.

$do = $db->prepare("INSERT INTO table (id, name) VALUES ".implode(',', array_fill(0, count($aData), '(?,?)')));

foreach($aData as $iIndex => $aValues){
 $iRealIndex = 2 * $iIndex + 1;
 $do->bindValue($iRealIndex, $aValues['id'], PDO::PARAM_INT);
 $iRealIndex = $iRealIndex + 1;
 $do->bindValue($iRealIndex, $aValues['name'], PDO::PARAM_STR);
}

$do->execute();

0

Bạn có thể chèn nhiều hàng trong một truy vấn duy nhất với chức năng này:

function insertMultiple($query,$rows) {
    if (count($rows)>0) {
        $args = array_fill(0, count($rows[0]), '?');

        $params = array();
        foreach($rows as $row)
        {
            $values[] = "(".implode(',', $args).")";
            foreach($row as $value)
            {
                $params[] = $value;
            }
        }

        $query = $query." VALUES ".implode(',', $values);
        $stmt = $PDO->prepare($query);
        $stmt->execute($params);
    }
}

$ row là một mảng các giá trị. Trong trường hợp của bạn, bạn sẽ gọi hàm với

insertMultiple("INSERT INTO tbl (`key1`,`key2`)",array(array('r1v1','r1v2'),array('r2v1','r2v2')));

Điều này có lợi ích là bạn sử dụng các câu lệnh đã chuẩn bị , trong khi chèn nhiều hàng với một truy vấn duy nhất. Bảo vệ!


0

Đây là giải pháp của tôi: https://github.com/sasha-ch/Aura.Sql dựa trên thư viện auraphp / Aura.Sql.

Ví dụ sử dụng:

$q = "insert into t2(id,name) values (?,?), ... on duplicate key update name=name"; 
$bind_values = [ [[1,'str1'],[2,'str2']] ];
$pdo->perform($q, $bind_values);

Bugreports được chào đón.


Kể từ phiên bản 2.4, bạn có thể tạo nhiều thao tác chèn với github.com/auraphp/Aura.SqlQuery/tree/iêu và sử dụng ExtendedPdo để thực thi :).
Hari KT

0

Ví dụ trong thế giới thực của tôi để chèn tất cả các mã bưu điện Đức vào một bảng trống (để thêm tên thị trấn sau):

// obtain column template
$stmt = $db->prepare('SHOW COLUMNS FROM towns');
$stmt->execute();
$columns = array_fill_keys(array_values($stmt->fetchAll(PDO::FETCH_COLUMN)), null);
// multiple INSERT
$postcode = '01000';// smallest german postcode
while ($postcode <= 99999) {// highest german postcode
    $values = array();
    while ($postcode <= 99999) {
        // reset row
        $row = $columns;
        // now fill our row with data
        $row['postcode'] = sprintf('%05d', $postcode);
        // build INSERT array
        foreach ($row as $value) {
            $values[] = $value;
        }
        $postcode++;
        // avoid memory kill
        if (!($postcode % 10000)) {
            break;
        }
    }
    // build query
    $count_columns = count($columns);
    $placeholder = ',(' . substr(str_repeat(',?', $count_columns), 1) . ')';//,(?,?,?)
    $placeholder_group = substr(str_repeat($placeholder, count($values) / $count_columns), 1);//(?,?,?),(?,?,?)...
    $into_columns = implode(',', array_keys($columns));//col1,col2,col3
    // this part is optional:
    $on_duplicate = array();
    foreach ($columns as $column => $row) {
        $on_duplicate[] = $column;
        $on_duplicate[] = $column;
    }
    $on_duplicate = ' ON DUPLICATE KEY UPDATE' . vsprintf(substr(str_repeat(', %s = VALUES(%s)', $count_columns), 1), $on_duplicate);
    // execute query
    $stmt = $db->prepare('INSERT INTO towns (' . $into_columns . ') VALUES' . $placeholder_group . $on_duplicate);//INSERT INTO towns (col1,col2,col3) VALUES(?,?,?),(?,?,?)... {ON DUPLICATE...}
    $stmt->execute($values);
}

Như bạn có thể thấy nó hoàn toàn linh hoạt. Bạn không cần kiểm tra số lượng cột hoặc kiểm tra vị trí cột của bạn. Bạn chỉ cần đặt dữ liệu chèn:

    $row['postcode'] = sprintf('%05d', $postcode);

Tôi tự hào về một số hàm tạo chuỗi truy vấn khi chúng hoạt động mà không có các hàm mảng nặng như Array_merge. Đặc biệt vsprintf () là một tìm kiếm tốt.

Cuối cùng tôi cần thêm 2x while () để tránh vượt quá giới hạn bộ nhớ. Điều này phụ thuộc vào giới hạn bộ nhớ của bạn, nhưng tất cả là một giải pháp chung tốt để tránh các vấn đề (và có 10 truy vấn vẫn tốt hơn 10.000).


0

test.php

<?php
require_once('Database.php');

$obj = new Database();
$table = "test";

$rows = array(
    array(
    'name' => 'balasubramani',
    'status' => 1
    ),
    array(
    'name' => 'balakumar',
    'status' => 1
    ),
    array(
    'name' => 'mani',
    'status' => 1
    )
);

var_dump($obj->insertMultiple($table,$rows));
?>

Cơ sở dữ liệu.php

<?php
class Database 
{

    /* Initializing Database Information */

    var $host = 'localhost';
    var $user = 'root';
    var $pass = '';
    var $database = "database";
    var $dbh;

    /* Connecting Datbase */

    public function __construct(){
        try {
            $this->dbh = new PDO('mysql:host='.$this->host.';dbname='.$this->database.'', $this->user, $this->pass);
            //print "Connected Successfully";
        } 
        catch (PDOException $e) {
            print "Error!: " . $e->getMessage() . "<br/>";
            die();
        }
    }
/* Insert Multiple Rows in a table */

    public function insertMultiple($table,$rows){

        $this->dbh->beginTransaction(); // also helps speed up your inserts.
        $insert_values = array();
        foreach($rows as $d){
            $question_marks[] = '('  . $this->placeholders('?', sizeof($d)) . ')';
            $insert_values = array_merge($insert_values, array_values($d));
            $datafields = array_keys($d);
        }

        $sql = "INSERT INTO $table (" . implode(",", $datafields ) . ") VALUES " . implode(',', $question_marks);

        $stmt = $this->dbh->prepare ($sql);
        try {
            $stmt->execute($insert_values);
        } catch (PDOException $e){
            echo $e->getMessage();
        }
        return $this->dbh->commit();
    }

    /*  placeholders for prepared statements like (?,?,?)  */

    function placeholders($text, $count=0, $separator=","){
        $result = array();
        if($count > 0){
            for($x=0; $x<$count; $x++){
                $result[] = $text;
            }
        }

        return implode($separator, $result);
    }

}
?>

Chào mừng đến với stackoverflow. Không chỉ là mã, xin vui lòng gửi những gì vấn đề của bạn và giải thích.
Prakash Palnati

về cơ bản. đó chỉ là một triển khai mã được cung cấp trong câu trả lời được chấp nhận
Ý thức chung của bạn

0

Tôi đã có cùng một vấn đề và đây là cách tôi hoàn thành cho chính mình, và tôi đã tạo ra một chức năng cho chính mình cho nó (và bạn có thể sử dụng nó nếu điều đó giúp bạn).

Thí dụ:

XÁC NHẬN VÀO các quốc gia (quốc gia, thành phố) GIÁ TRỊ (Đức, Berlin), (Pháp, Paris);

$arr1 = Array("Germany", "Berlin");
$arr2 = Array("France", "France");

insertMultipleData("countries", Array($arr1, $arr2));


// Inserting multiple data to the Database.
public function insertMultipleData($table, $multi_params){
    try{
        $db = $this->connect();

        $beforeParams = "";
        $paramsStr = "";
        $valuesStr = "";

        for ($i=0; $i < count($multi_params); $i++) { 

            foreach ($multi_params[$i] as $j => $value) {                   

                if ($i == 0) {
                    $beforeParams .=  " " . $j . ",";
                }

                $paramsStr .= " :"  . $j . "_" . $i .",";                                       
            }

            $paramsStr = substr_replace($paramsStr, "", -1);
            $valuesStr .=  "(" . $paramsStr . "),"; 
            $paramsStr = "";
        }


        $beforeParams = substr_replace($beforeParams, "", -1);
        $valuesStr = substr_replace($valuesStr, "", -1);


        $sql = "INSERT INTO " . $table . " (" . $beforeParams . ") VALUES " . $valuesStr . ";";

        $stmt = $db->prepare($sql);


        for ($i=0; $i < count($multi_params); $i++) { 
            foreach ($multi_params[$i] as $j => &$value) {
                $stmt->bindParam(":" . $j . "_" . $i, $value);                                      
            }
        }

        $this->close($db);
        $stmt->execute();                       

        return true;

    }catch(PDOException $e){            
        return false;
    }

    return false;
}

// Making connection to the Database 
    public function connect(){
        $host = Constants::DB_HOST;
        $dbname = Constants::DB_NAME;
        $user = Constants::DB_USER;
        $pass = Constants::DB_PASS;

        $mysql_connect_str = 'mysql:host='. $host . ';dbname=' .$dbname;

        $dbConnection = new PDO($mysql_connect_str, $user, $pass);
        $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        return $dbConnection;
    }

    // Closing the connection
    public function close($db){
        $db = null;
    }

Nếu insertMult MônData (bảng $, $ multi_params) trả về TRUE , dữ liệu của bạn đã được chèn vào cơ sở dữ liệu của bạn.


0

Dựa trên các thử nghiệm của tôi, tôi phát hiện ra rằng câu lệnh chèn mysql với nhiều hàng giá trị trong một giao dịch là cách nhanh nhất.

Tuy nhiên, nếu dữ liệu quá nhiều thì max_allowed_packetcài đặt của mysql có thể hạn chế chèn giao dịch đơn lẻ với nhiều hàng giá trị. Do đó, các hàm sau sẽ thất bại khi có dữ liệu lớn hơn max_allowed_packetkích thước của mysql :

  1. singleTransactionInsertWithRollback
  2. singleTransactionInsertWithPlaceholders
  3. singleTransactionInsert

Phương pháp thành công nhất trong việc chèn kịch bản dữ liệu khổng lồ là transactionSpeedphương thức, nhưng nó tiêu tốn nhiều thời gian hơn các phương thức đã đề cập ở trên. Vì vậy, để xử lý vấn đề này, bạn có thể chia dữ liệu của mình thành các phần nhỏ hơn và gọi giao dịch đơn lẻ chèn nhiều lần hoặc từ bỏ tốc độ thực hiện bằng transactionSpeedphương pháp.

Đây là nghiên cứu của tôi

<?php

class SpeedTestClass
{
    private $data;

    private $pdo;

    public function __construct()
    {
        $this->data = [];
        $this->pdo = new \PDO('mysql:dbname=test_data', 'admin', 'admin');
        if (!$this->pdo) {
            die('Failed to connect to database');
        }
    }

    public function createData()
    {
        $prefix = 'test';
        $postfix = 'unicourt.com';
        $salutations = ['Mr.', 'Ms.', 'Dr.', 'Mrs.'];

        $csv[] = ['Salutation', 'First Name', 'Last Name', 'Email Address'];
        for ($i = 0; $i < 100000; ++$i) {
            $csv[] = [
                $salutations[$i % \count($salutations)],
                $prefix.$i,
                $prefix.$i,
                $prefix.$i.'@'.$postfix,
            ];
        }

        $this->data = $csv;
    }

    public function truncateTable()
    {
        $this->pdo->query('TRUNCATE TABLE `name`');
    }

    public function transactionSpeed()
    {
        $timer1 = microtime(true);
        $this->pdo->beginTransaction();
        $sql = 'INSERT INTO `name` (`first_name`, `last_name`) VALUES (:first_name, :last_name)';
        $sth = $this->pdo->prepare($sql);

        foreach (\array_slice($this->data, 1) as $values) {
            $sth->execute([
                ':first_name' => $values[1],
                ':last_name' => $values[2],
            ]);
        }

        // $timer2 = microtime(true);
        // echo 'Prepare Time: '.($timer2 - $timer1).PHP_EOL;
        // $timer3 = microtime(true);

        if (!$this->pdo->commit()) {
            echo "Commit failed\n";
        }
        $timer4 = microtime(true);
        // echo 'Commit Time: '.($timer4 - $timer3).PHP_EOL;

        return $timer4 - $timer1;
    }

    public function autoCommitSpeed()
    {
        $timer1 = microtime(true);
        $sql = 'INSERT INTO `name` (`first_name`, `last_name`) VALUES (:first_name, :last_name)';
        $sth = $this->pdo->prepare($sql);
        foreach (\array_slice($this->data, 1) as $values) {
            $sth->execute([
                ':first_name' => $values[1],
                ':last_name' => $values[2],
            ]);
        }
        $timer2 = microtime(true);

        return $timer2 - $timer1;
    }

    public function noBindAutoCommitSpeed()
    {
        $timer1 = microtime(true);

        foreach (\array_slice($this->data, 1) as $values) {
            $sth = $this->pdo->prepare("INSERT INTO `name` (`first_name`, `last_name`) VALUES ('{$values[1]}', '{$values[2]}')");
            $sth->execute();
        }
        $timer2 = microtime(true);

        return $timer2 - $timer1;
    }

    public function singleTransactionInsert()
    {
        $timer1 = microtime(true);
        foreach (\array_slice($this->data, 1) as $values) {
            $arr[] = "('{$values[1]}', '{$values[2]}')";
        }
        $sth = $this->pdo->prepare('INSERT INTO `name` (`first_name`, `last_name`) VALUES '.implode(', ', $arr));
        $sth->execute();
        $timer2 = microtime(true);

        return $timer2 - $timer1;
    }

    public function singleTransactionInsertWithPlaceholders()
    {
        $placeholders = [];
        $timer1 = microtime(true);
        $sql = 'INSERT INTO `name` (`first_name`, `last_name`) VALUES ';
        foreach (\array_slice($this->data, 1) as $values) {
            $placeholders[] = '(?, ?)';
            $arr[] = $values[1];
            $arr[] = $values[2];
        }
        $sql .= implode(', ', $placeholders);
        $sth = $this->pdo->prepare($sql);
        $sth->execute($arr);
        $timer2 = microtime(true);

        return $timer2 - $timer1;
    }

    public function singleTransactionInsertWithRollback()
    {
        $placeholders = [];
        $timer1 = microtime(true);
        $sql = 'INSERT INTO `name` (`first_name`, `last_name`) VALUES ';
        foreach (\array_slice($this->data, 1) as $values) {
            $placeholders[] = '(?, ?)';
            $arr[] = $values[1];
            $arr[] = $values[2];
        }
        $sql .= implode(', ', $placeholders);
        $this->pdo->beginTransaction();
        $sth = $this->pdo->prepare($sql);
        $sth->execute($arr);
        $this->pdo->commit();
        $timer2 = microtime(true);

        return $timer2 - $timer1;
    }
}

$s = new SpeedTestClass();
$s->createData();
$s->truncateTable();
echo "Time Spent for singleTransactionInsertWithRollback: {$s->singleTransactionInsertWithRollback()}".PHP_EOL;
$s->truncateTable();
echo "Time Spent for single Transaction Insert: {$s->singleTransactionInsert()}".PHP_EOL;
$s->truncateTable();
echo "Time Spent for single Transaction Insert With Placeholders: {$s->singleTransactionInsertWithPlaceholders()}".PHP_EOL;
$s->truncateTable();
echo "Time Spent for transaction: {$s->transactionSpeed()}".PHP_EOL;
$s->truncateTable();
echo "Time Spent for AutoCommit: {$s->noBindAutoCommitSpeed()}".PHP_EOL;
$s->truncateTable();
echo "Time Spent for autocommit with bind: {$s->autoCommitSpeed()}".PHP_EOL;
$s->truncateTable();

Kết quả cho 100.000 mục nhập cho một bảng chỉ chứa hai cột như dưới đây

$ php data.php
Time Spent for singleTransactionInsertWithRollback: 0.75147604942322
Time Spent for single Transaction Insert: 0.67445182800293
Time Spent for single Transaction Insert With Placeholders: 0.71131205558777
Time Spent for transaction: 8.0056409835815
Time Spent for AutoCommit: 35.4979159832
Time Spent for autocommit with bind: 33.303519010544

0

Điều này làm việc cho tôi

$sql = 'INSERT INTO table(pk_pk1,pk_pk2,date,pk_3) VALUES '; 
$qPart = array_fill(0, count($array), "(?, ?,UTC_TIMESTAMP(),?)");
$sql .= implode(",", $qPart);
$stmt =    DB::prepare('base', $sql);
$i = 1;
foreach ($array as $value) { 
  $stmt->bindValue($i++, $value);
  $stmt->bindValue($i++, $pk_pk1);
  $stmt->bindValue($i++, $pk_pk2); 
  $stmt->bindValue($i++, $pk_pk3); 
} 
$stmt->execute();

0

những gì như thế này:

        if(count($types_of_values)>0){
         $uid = 1;
         $x = 0;
         $sql = "";
         $values = array();
          foreach($types_of_values as $k=>$v){
            $sql .= "(:id_$k,:kind_of_val_$k), ";
            $values[":id_$k"] = $uid;
            $values[":kind_of_val_$k"] = $v;
          }
         $sql = substr($sql,0,-2);
         $query = "INSERT INTO table (id,value_type) VALUES $sql";
         $res = $this->db->prepare($query);
         $res->execute($values);            
        }

Ý tưởng đằng sau điều này là xoay vòng các giá trị mảng của bạn, thêm "số id" vào mỗi vòng lặp cho các vị trí giữ câu lệnh đã chuẩn bị của bạn, đồng thời, bạn thêm các giá trị vào mảng cho các tham số liên kết. Nếu bạn không thích sử dụng chỉ mục "khóa" từ mảng, bạn có thể thêm $ i = 0 và $ i ++ trong vòng lặp. Hoặc là hoạt động trong ví dụ này, ngay cả khi bạn có mảng kết hợp với các khóa được đặt tên, nó vẫn hoạt động với việc cung cấp các khóa là duy nhất. Với một công việc nhỏ, nó cũng sẽ tốt cho các mảng lồng nhau ..

** Lưu ý rằng đế loại bỏ các biến $ sql không gian cuối cùng và dấu phẩy, nếu bạn không có khoảng trắng, bạn cần thay đổi giá trị này thành -1 thay vì -2.


-1

Hầu hết các giải pháp được đưa ra ở đây để tạo truy vấn đã chuẩn bị phức tạp hơn mà chúng cần phải có. Sử dụng các hàm dựng sẵn của PHP, bạn có thể dễ dàng tạo ra câu lệnh SQL mà không cần chi phí đáng kể.

Cho trước $records, một mảng các bản ghi trong đó mỗi bản ghi là một mảng được lập chỉ mục (ở dạng field => value), hàm sau sẽ chèn các bản ghi vào bảng đã cho $table, trên một kết nối PDO $connection, chỉ sử dụng một câu lệnh được chuẩn bị duy nhất. Lưu ý rằng đây là một giải pháp PHP 5.6+ do sử dụng giải nén đối số trong lệnh gọi tới array_push:

private function import(PDO $connection, $table, array $records)
{
    $fields = array_keys($records[0]);
    $placeHolders = substr(str_repeat(',?', count($fields)), 1);
    $values = [];
    foreach ($records as $record) {
        array_push($values, ...array_values($record));
    }

    $query = 'INSERT INTO ' . $table . ' (';
    $query .= implode(',', $fields);
    $query .= ') VALUES (';
    $query .= implode('),(', array_fill(0, count($records), $placeHolders));
    $query .= ')';

    $statement = $connection->prepare($query);
    $statement->execute($values);
}

1
Không bao giờ nên sử dụng mã này vì nó dễ bị tấn công SQL
Ý thức chung của bạn
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.