2009-04-23 3 views
111

Я передаю большой набор данных в таблицу mysql через php, используя команды вставки, и мне интересно, можно ли вставить примерно 1000 строк за один раз с помощью запроса, отличного от добавления каждое значение в конце строки длиной в милю, а затем выполняет ее. Я использую фреймворк codeigniter, поэтому его функции также доступны мне.Вставить несколько строк через php-массив в mysql

+0

Я дал ответ по вашему вопросу для многократного вставки строк CodeIgniter в. –

+0

@SomnathMuluk Спасибо, однако прошло какое-то время, так как мне нужно было ответить на этот вопрос:) ... – toofarsideways

+0

Я бы рекомендовал использовать функцию insert_batch CodeIgniter. Если вы используете библиотеку, всегда старайтесь использовать ее сильные стороны и стандарты кодирования. –

ответ

202

Сборка одного оператора 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() для назначения предложения значения внешнему массиву.

+4

Спасибо за это! Кстати, у вас отсутствует закрывающая скобка в конце функции, если кто-то планирует ее скопировать. mysql_real_query ('INSERT INTO table VALUES (текст, категория)' .implode (','. $ sql)); – toofarsideways

+3

Спасибо! Исправлена. (Я часто это делаю ...) – staticsan

+0

Я также заметил, что я тоже глупый implode (','. $ Sql); должен быть imode (',', $ sql); – toofarsideways

15

Вы можете подготовить запрос для вставки одной строки с использованием класса 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 'являются типами данных, которые вы связывающими (интермедиат, двойной, строки, BLOB).

+3

Недавно я провел несколько тестов, сравнивающих объемные вставки и подготовленные вставки, как указано здесь. Для около 500 вставок метод подготовленных вставок завершен между 2,6-4,4 сек и методом объемной вставки в 0,12-0,35 сек. Я бы подумал, что mysql будет «насыпать» эти подготовленные операторы вместе и выполнять так же, как и объемные вставки, но при настройке по умолчанию разница в производительности огромна. (Btw все тестовые запросы выполнялись внутри одной транзакции для каждого теста, чтобы предотвратить автоматическое комментирование) – Motin

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

+0

@cherouvim ... umm, спасибо? Этот пост больше года назад ... – bdl

+1

Да. Информация остается навсегда, хотя :) – cherouvim

7

Вы всегда можете использовать в 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 заявлений.

+0

Я изучил это, но мне нужно манипулировать данными перед его вставкой. Это было дано мне как декартово произведение 1400 на 1400 наборов значений int, многие из которых равны нулю. Мне нужно преобразовать это ко многим отношениям, используя промежуточную таблицу для экономии места, следовательно, необходимость в вставках в отличие от массовой вставки – toofarsideways

+0

Вы можете всегда генерировать CSV-файл после его обработки и вызывать оператор mysql, который загружает данные –

+0

Я думаю, что полезно знать, что путь локален для вашего SQL-клиента, а не на сервере SQL. Файл загружается на сервер, а затем читается им. Я думал, что файл должен быть уже на сервере, что не так. Если он уже находится на сервере, удалите бит «LOCAL». – Kyle

13

Я знаю, что это старый вопрос, но я просто читал и думал, я хотел бы добавить, что я нашел в другом месте:

MySQLi в PHP 5 является ojbect с хорошими функциями, которые позволят вам ускорить время вставки для ответа выше:

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

Отключения автоматической фиксации при вставке большого количества строк значительно ускоряет вставку, поэтому выключить его, а затем выполнить, как указано выше, или просто сделать строку (sqlCombined), который многие вставки операторы, разделенные полуколониями и несколькими запросами, прекрасно справятся с ними.

Надеюсь, это поможет кому-то сэкономить время (поиск и вставка!)

R

+0

Это ошибка, которую я получил по вашей идее: «Неустранимая ошибка: вызов функции-члена autocommit() на null в /homepages/25/d402746174/htdocs/MoneyMachine/saveQuotes.php в строке 30« – user3217883

53

Множественные вставки/пакетной вставки теперь поддерживается 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') 
+1

Я думаю, что это наиболее рекомендуемый способ сделать многострочную вставку, используя mysql_query. Потому что, когда мы используем фреймворк, хорошей практикой всегда является использование встроенных функций фреймворка. –

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)); 
-3
use this in codeigniter for multiple data insertion 


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

И как это отличается от ответа Сомната? Infact, это его копия – asprin

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

Используйте вставку в кодировщике для вставки нескольких строк d ата.

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

Вы можете сделать это несколькими способами в кодеигниторе, например.

First By loop

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

Second -- By insert batch

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

Third way -- By multiple value pass

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

Хотя на этот вопрос еще слишком поздно. Вот мой ответ на то же самое.

Если вы используете 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 here

Смежные вопросы