вставити кілька рядків через масив php в mysql


129

Я передаю великий набір даних у таблицю MySQL через PHP за допомогою команд вставки, і мені цікаво, чи можливо вставити приблизно 1000 рядків одночасно за допомогою запиту, окрім додавання кожного значення у кінці пробігу милі, а потім виконуючи його. Я використовую рамку CodeIgniter, тому її функції мені також доступні.


Я дав відповідь на ваше запитання для вставки декількох рядків Codeigniter.
Сомнат Мулук

@SomnathMuluk Дякую, проте минув час, оскільки мені потрібно було відповісти на це питання:) ...
toofarsideways

Я рекомендую використовувати функцію insert_batch CodeIgniter. Якщо ви використовуєте бібліотеку, завжди намагайтеся використовувати її сильні сторони та стандарти кодування.
Дьюальд Елс

Я вважаю, що вставка партії - найкращий спосіб зробити це, перегляньте посилання 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 ('ВСТАВЛЯЄТЬСЯ в таблицю VALUES (текст, категорія)' .implode (','. $ sql));
toofarsideways

3
Дякую! Виправлено. (Я часто це роблю ...)
staticsan

1
а запит справді повинен бути "ВСТАВЛЯЄТЬСЯ в таблицю (текст, категорія) ЗНАЧЕННЯ" .implode (','. $ sql) 'кодування зітхання 4:00 веде до жахливої ​​налагодження :(
toofarsideways

3
Я вірю, що цей код створить рішення для мого останнього проекту. Моє запитання тут, чи це безпечно від ін'єкції SQL? Мої плани складаються з вимикання mysql_real_escape_stringз mysqli_real_escape_stringі mysql_queryз, mysqli_queryяк я використовую MySQLi, і вони застаріли, як у PHP5. Велике дякую!
словник

2
mysql_*було видалено з PHP, тому обов'язково використовуйте mysqli_*інтерфейс.
Рік Джеймс

60

Зараз декілька вставок / пакетних вставок підтримується кодигінером. У мене була така ж проблема. Хоча відповісти на питання дуже пізно, це комусь допоможе. Ось чому, відповідаючи на це питання.

$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. Тому що, коли ми використовуємо рамку, це хороша практика завжди використовувати вбудовані функції рамки.
Пранеет Нідаршан

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 - це ojbect з деякими хорошими функціями, які дозволять прискорити час вставки для відповіді вище:

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

Вимкнення автокомісії під час вставки багатьох рядків значно пришвидшує вставку, тому вимкніть її, потім виконайте, як було сказано вище, або просто зробіть рядок (sqlCombined), який містить безліч операцій вставлення, розділених напівколонами і багато запит буде справляти їх добре.

Сподіваємось, це допоможе комусь заощадити час (пошук та вставлення!)

R


Це помилка, яку я отримав, використовуючи вашу ідею: "Фатальна помилка: зателефонуйте в функцію члена autocommit () на нулі в /homepages/25/d402746174/htdocs/MoneyMachine/saveQuotes.php у рядку 30"
користувач3217883

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 на 1400, багато з яких дорівнюють нулю. Мені потрібно перетворити це на багато-багато стосунків, використовуючи посередницьку таблицю, щоб заощадити простір, отже, потреба в вставленнях, на відміну від об'ємної вставки
toofarsideways

Ви завжди можете згенерувати файл CSV після маніпулювання ним та виклику оператора mysql, який завантажує дані
Олександр Жардим,

Я думаю, що корисно знати, що шлях локальний до вашого клієнта SQL, а не до сервера SQL. Файл завантажується на сервер і потім читається ним. Я думав, що файл вже повинен бути на сервері, що не так. Якщо він вже є на сервері, видаліть LOCALбіт.
Кайл

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 ($ k> 0).
cherouvim

@cherouvim ... Ну, ти маєш рацію з цим. Дякуємо за ваш внесок Перечитуючи приклад, який я наводив, я не бачу вашої точки зору. Обережно розробити (за допомогою пастебіну тощо). Спасибі-
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

Ви можете зробити це декількома способами в кодігінітер, наприклад

Спочатку по петлі

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

Використовуйте пакет вставити в кодигнатор, щоб вставити кілька рядків даних.

$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));
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.