вставить несколько строк через массив php в mysql


129

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


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

@SomnathMuluk Спасибо, но прошло много времени с тех пор, как мне нужно было ответить на этот вопрос
:)

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

Я считаю, что пакет вставки - лучший способ сделать это. См. Ссылку stackoverflow.com/questions/27206178/codeigniter-insert-batch
Сайед Амир Бухари

Ответы:


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
Спасибо за это! Кстати, вам не хватает закрывающей скобки в конце функции, если кто-то планирует ее скопировать. mysql_real_query ('INSERT INTO table VALUES (text, category)' .implode (','. $ sql));
toofarsideways

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

1
и запрос действительно должен быть 'INSERT INTO table (text, category) VALUES' .implode (','. $ sql) 'sigh Кодирование в 4 часа
ночи

3
Я считаю, что этот код создаст решение для моего последнего проекта. У меня вопрос: безопасно ли это от SQL-инъекции? Мои планы , чтобы перейти из mysql_real_escape_stringс mysqli_real_escape_stringи mysql_queryс , mysqli_queryкак я использую MySQLi и они были устаревшим PHP5. Большое спасибо!
wordman

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

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 «объединил» эти подготовленные операторы вместе и будет работать так же хорошо, как и массовые вставки, но при настройке по умолчанию разница в производительности, очевидно, огромна. (Между прочим, все тестируемые запросы выполнялись внутри одной транзакции для каждого теста, чтобы предотвратить автоматическую фиксацию)
Motin

16

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

mysqli в PHP 5 - это объект с некоторыми хорошими функциями, которые позволят вам ускорить время вставки для ответа выше:

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

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

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

р


Это ошибка, которую я получил, используя вашу идею: «Неустранимая ошибка: вызов функции-члена autocommit () с нулевым значением в /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 на 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 над запросом и измените его на что-то вроде if ($ k> 0).
cherouvim 02

@cherouvim ... Ну, в этом ты прав. Спасибо за ваш вклад. Перечитывая приведенный мной пример, я не понимаю вашей точки зрения. Позаботьтесь о разработке (через pastebin и т. Д.?). Спасибо-
bdl 03

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, вы можете использовать встроенные методы, определенные в классе 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

Используйте пакет вставки в 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));
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.