How to insert multiple rows from array using CodeIgniter framework? - php

I'm passing a large dataset into a MySQL table via PHP using insert commands and I'm wondering if it's possible to insert approximately 1000 rows at a time via a query other than appending each value on the end of a mile-long string and then executing it. I am using the CodeIgniter framework so its functions are also available to me.

Assembling one INSERT statement with multiple rows is much faster in MySQL than one INSERT statement per row.
That said, it sounds like you might be running into string-handling problems in PHP, which is really an algorithm problem, not a language one. Basically, when working with large strings, you want to minimize unnecessary copying. Primarily, this means you want to avoid concatenation. The fastest and most memory efficient way to build a large string, such as for inserting hundreds of rows at one, is to take advantage of the implode() function and array assignment.
$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));
The advantage of this approach is that you don't copy and re-copy the SQL statement you've so far assembled with each concatenation; instead, PHP does this once in the implode() statement. This is a big win.
If you have lots of columns to put together, and one or more are very long, you could also build an inner loop to do the same thing and use implode() to assign the values clause to the outer array.

Multiple insert/ batch insert is now supported by 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')

You could prepare the query for inserting one row using the mysqli_stmt class, and then iterate over the array of data. Something like:
$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();
Where 'idsb' are the types of the data you're binding (int, double, string, blob).

mysqli in PHP 5 is an object with some good functions that will allow you to speed up the insertion time for the answer above:
$mysqli->autocommit(FALSE);
$mysqli->multi_query($sqlCombined);
$mysqli->autocommit(TRUE);
Turning off autocommit when inserting many rows greatly speeds up insertion, so turn it off, then execute as mentioned above, or just make a string (sqlCombined) which is many insert statements separated by semi-colons and multi-query will handle them fine.

You could always use mysql's LOAD DATA:
LOAD DATA LOCAL INFILE '/full/path/to/file/foo.csv' INTO TABLE `footable` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n'
to do bulk inserts rather than using a bunch of INSERT statements.

Well, you don't want to execute 1000 query calls, but doing this is fine:
$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);
Depending on your data source, populating the array might be as easy as opening a file and dumping the contents into an array via file().

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

You can do it with several ways in codeigniter e.g.
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));

Although it is too late to answer this question. Here are my answer on the same.
If you are using CodeIgniter then you can use inbuilt methods defined in query_builder class.
$this->db->insert_batch()
Generates an insert string based on the data you supply, and runs the query. You can either pass an array or an object to the function. Here is an example using an array:
$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')
The first parameter will contain the table name, the second is an associative array of values.
You can find more details about query_builder here

I have created a class that performs multi-line that is used as follows:
$pdo->beginTransaction();
$pmi = new PDOMultiLineInserter($pdo, "foo", array("a","b","c","e"), 10);
$pmi->insertRow($data);
// ....
$pmi->insertRow($data);
$pmi->purgeRemainingInserts();
$pdo->commit();
where the class is defined as follows:
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;
}
}

Use insert batch in codeigniter to insert multiple row of data.
$this->db->insert_batch('tabname',$data_array); // $data_array holds the value to be inserted

I had to INSERT more than 14000 rows into a table and found that line for line with Mysqli prepared statements took more than ten minutes, while argument unpacking with string parameters for the same Mysqli prepared statements did it in less than 10 seconds. My data was very repetitive as it was multiples of id's and one constant integer.
10 minutes code:
$num = 1000;
$ent = 4;
$value = ['id' => 1,
'id' => 2,
'id' => 3,
'id' => 4,
'id' => 5,
'id' => 6,
'id' => 7,
'id' => 8,
'id' => 9,
'id' => 10,
'id' => 11,
'id' => 12,
'id' => 13,
'id' => 14];
$cnt = 0;
$query = "INSERT INTO table (col1, col2) VALUES (?,?)";
$stmt = $this->db->prepare($query);
$stmt->bind_param('ii', $arg_one,$arg_two);
foreach ($value as $k => $val) {
for ($i=0; $i < $num; $i++) {
$arg_one = $k;
$arg_two = $ent;
if($stmt->execute()) {
$cnt++;
}
}
}
10 second code:
$ent = 4;
$num = 1000;
$value = ['id' => 1,
'id' => 2,
'id' => 3,
'id' => 4,
'id' => 5,
'id' => 6,
'id' => 7,
'id' => 8,
'id' => 9,
'id' => 10,
'id' => 11,
'id' => 12,
'id' => 13,
'id' => 14];
$newdat = [];
foreach ($value as $k => $val) {
for ($i=0; $i < $num; $i++) {
$newdat[] = $val;
$newdat[] = $ent;
}
}
// create string of data types
$cnt = count($newdat);
$param = str_repeat('i',$cnt);
// create string of question marks
$rec = (count($newdat) == 0) ? 0 : $cnt / 2 - 1;
$id_q = str_repeat('(?,?),', $rec) . '(?,?)';
// insert
$query = "INSERT INTO table (col1, col2) VALUES $id_q";
$stmt = $db->prepare($query);
$stmt->bind_param($param, ...$newdat);
$stmt->execute();

I have created this simple function which you guys can use easily. You will need to pass the table-name ($tbl), table-field ($insertFieldsArr) against your inserting data, data array ($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));
}

Related

How to check if arrays values match while preserving its order in PHP

What I'm trying to do is preserve the order of an array, while determining if each of the numbers match numbers that were pulled from the database. What I have is an array of numbers ($ids), and a non empty array of rows ($rows)
$images = array();
$ids = array(
0 => '41',
1 => '42',
2 => '43',
3 => '44'
);
// database example
$rows = array(
0 => array(
'name' => 'John Doe',
'id' => '42'
),
1 => array(
'name' => 'Jane Doe',
'id' => '43'
),
);
$i = 0;
foreach ($rows as $row) {
// This works, but it doesn't preserve the order of the $ids array
if (in_array($row['id'], $ids[$i])) {
// add $id to a new array
// or use the same $id array
$images[$i]['name'] = $row['name'];
$images[$i]['id'] = $row['id'];
$i++;
}
}
Any help would be appreciated.
Since id is unique in the rows from your database, you can index the $rows array by id to make it easier to look up values there. You can do this with array_column like this:
$rows = array_column($rows, null, 'id');
Or add them to $rows using id as index as you fetch them from the db like this:
while ($row = //whatever query result/prepared statement fetch you're using) {
$rows[$row['id']] = $row;
}
Then, instead of iterating $rows, you can iterate $ids. This will ensure that the resulting $images array will be in the same order as $ids.
foreach ($ids as $id) {
if (isset($rows[$id])) {
$images[] = [
'name' => $rows[$id]['name'],
'id' => $rows[$id]['id']
];
}
}
Another thing that might make this easier (unless you're using the non-matching rows for something else), would be to use the $ids array as a parameter to a WHERE id IN clause in the query that's selecting $rows, so you won't have to do that filtering in PHP. Here's a Q&A that shows how to do that with PDO, for example: PHP - Using PDO with IN clause array.
You could iterate over ids array instead. It will be something like this:
foreach($ids as $id) {
$data = find($id, $rows);
if ($data === null) {
continue;
}
$images[] = [
'name' => $data['name'],
'id' => $data['id']
];
}
function find($id, $rows) {
foreach($rows as $row) {
if($row['id'] == $id) {
return $row;
}
}
return null;
}
Totally agree with #Don't Panic, you should fetch only the data you'll use if possible.

Parse php array to make insert statement

Hi I have an array like this:
$datas =
[
'name_1'=>'John',
'name_2' =>'Mickey',
'settings_1' => 'Settings 1',
'settings_2' => 'Settings 2'
]
foreach($datas as $data){
//get items here...
}
How to pair or parse those items to make insert statement like this:
INSERT INTO table (name, settings)VALUES('John','Settings 1');
INSERT INTO table (name, settings)VALUES('Mickey','Settings 2');
Any idea? Thanks in advance.
This code could be usefull for creating array of arrays. Considering array keys will be name_x and settings_x
foreach($datas as $key=>$value){
// explode each key of the array
$keys = explode('_',$key);
$name = 'name_'.$keys[1];
$settings = 'settings_'.$keys[1];
// to generate array
$new_array[$keys[1]]=array('name'=>$datas[$name], 'settings'=>$datas[$settings]);
}
print_r($new_array);
Loop the $new_array for insert query.
Output :
Array
(
[1] => Array
(
[name] => John
[settings] => Settings 1
)
[2] => Array
(
[name] => Mickey
[settings] => Settings 2
)
)
$datas =
[
'name_1'=>'John',
'name_2' =>'Mickey',
'settings_1' => 'Settings 1',
'settings_2' => 'Settings 2'
];
$results = [];
foreach($datas as $index=>$data){
//create an array out of $index breaking it at the '_'
//so array will be $parts = [0=>'name', 1=>'1'];
$parts = explode('_', $index);
//'name_1'=>'John' = $results[1]['name'] = 'John';
$results[$parts[1]][$parts[0]] = $data;
}
//batch insert new formed array
//INSERT INTO tbl_name (name, settings) VALUES $results;
Check this, you must do some intermediate steps. Comments on code!!
$datas =
[
'name_1'=>'John',
'name_2' =>'Mickey',
'settings_1' => 'Settings 1',
'settings_2' => 'Settings 2'
];
$data_final = [];
foreach($datas as $key=>$value){
$keyX = preg_replace('/^(.+)_(\d+)$/', '$1', $key);// get the "type" (name, setting)
$keyY = preg_replace('/^(.+)_(\d+)$/', '$2', $key);// get the "index" (_1, _2 without "_")
$data_final[$keyY][$keyX] = $value; // put in result
}
// make queries
$sql = [];
foreach($data_final as $datas){
$fields = implode(", ", array_keys($datas)); //implode the keys to sql fields
$values = implode(", ", array_map(function($a){return "'$a'";}, array_values($datas)));//implode values to sql values adding ''. WARNING: This not escape values. Use escape string function on mysqli obj or PDO to the right escape
$sql[] = "INSERT INTO table ($fields) VALUES ($values);"; // populate query
}
$sql = implode("\n", $sql); // implode with new line
print_r($sql); //results
IMPORTANT:
You must have the right syntax "field_number" to respect the procedure
You can use even with one or more of two fields per record
You can use any field name, always respecting the "field_number" syntax
DEMO HERE

Inserting multiple arrays into a mysql database

Each input->post is a key/value array. Each value inside of each array matches up to the rest of the array values. So the first row in the database insertion will be all of the data from all of the array's key 0. The second will be 1 and so on and so on. How would I take all of the data from the post arrays and insert them into the database using this method?
$selected_size = $this->input->post('selected_size');
$product_id = $this->input->post('product_id');
$product_name = $this->input->post('product_name');
$product_color = $this->input->post('product_color');
$cookie_id = $this->input->cookie("cookie_id");
$q1 = $this->db->query("SELECT * FROM default_cart_temp
WHERE cookie_id = '$cookie_id'
AND is_paid = 'No'");
if ($q1->num_rows() > 0) {
} else {
print_r($selected_size);
/*$data = array('selected_size' => $selected_size,
'product_id' => $product_id,
'product_name' => $product_name,
'product_color' => $product_color);
$this->db->insert('mytable', $data);*/
}
Assuming your insert() method takes an associative array and then builds and executes the insert statement:
foreach($product_id as $key => $value)
{
$data = array(
'selected_size' => $selected_size[$key],
'product_id' => $value,
'product_name' => $product_name[$key],
'product_color' => $product_color[$key]
);
$this->db->insert('mytable', $data);
}
By using a foreach on one of the arrays and obtaining the key on each iteration, you can use the same key variable to access the corresponding element in each other array.

Fetch array using both if and while doesn't work

Basically I have 2 methods in the same class, getMovie and getGenres. They are very similar but One doesn't return what I expect.
Here's getMovie method:
public function getMovie($argType, $arg){
$movieQuery = "SELECT id,
rt_id,
imdb_id,
url,
rt_url,
type,
adult,
DATE_FORMAT(release_date, '%Y') AS year,
date_added,
title,
runtime,
budget,
revenue,
homepage,
rating,
tagline,
overview,
popularity,
image,
backdrop,
trailer
FROM movies
WHERE " . $argType . " = " . $arg;
$movieResult = $this->_query($movieQuery);
$movies = array();
if($movieResult->fetch_array(MYSQLI_ASSOC)){
while($m = $movieResult->fetch_array(MYSQLI_ASSOC)){
$movies[] = array( 'title' => $m['title'],
'duplicate' => $m['duplicate'],
'url' => $m['url'],
'rt_url' => $m['rt_url'],
'release_date' => $m['release_date'],
'date_added' => $m['date_added'],
'type' => 'movie',
'adult' => $m['adult'],
'id' => $id,
'rt_id' => $m['rt_id'],
'imdb_id' => $m['imdb_id'],
'rating' => $m['rating'],
'tagline' => $m['tagline'],
'overview' => $m['overview'],
'popularity' => $m['popularity'],
'runtime' => $m['runtime'],
'budget' => $m['budget'],
'revenue' => $m['revenue'],
'homepage' => $m['homepage'],
'image' => $m['image'],
'backdrop' => $m['backdrop'],
'trailer' => $m['trailer'] );
}
return $movies;
}
else{
return false;
}
Here's getGenres method:
public function getGenres($movieId = NULL){
$genresQuery = "";
if($movieId != NULL){
$genresQuery = "SELECT id,
name
FROM genres
WHERE id = ANY (
SELECT genre_id
FROM movie_genres
WHERE movie_id = " . $movieId . ")";
}
else{
$genresQuery = "SELECT id,
name
FROM genres";
}
$genresResult = $this->_query($genresQuery);
$genres = array();
if($genresResult->fetch_array(MYSQLI_ASSOC)){
while($genre = $genresResult->fetch_array(MYSQLI_ASSOC)){
$genres[] = array( 'id' => $genre['id'],
'name' => $genre['name'] );
}
return $genres;
}
else{
return false;
}
}
And here's how I call them:
$mov = $movie->getMovie(2207);
print_r($mov); // output: Array()
$gen = $movie->getGenres(2207);
print_r($gen); // output: Array(values inside)
Both queries do actually return expected values but getMovies method doesn't work with the if statement. It works fine if I just have while loop.
I am using if as well as while as I heard that while loop can sometimes execute even when there's not values. Is there any truth to this? If there is indeed a reason to use an if statement as well as wile loop then why doesn't it work with getMovies method?
Edit 1: I tried storing the array like so but that resulted in a memory related error:
$r = $genresResult->fetch_array(MYSQLI_ASSOC);
if($r){
while($r){
$genres[] = array( 'id' => $genre['id'],
'name' => $genre['name'] );
}
return $genres;
}
I am using if as well as while as I heard that while loop can sometimes execute even when there's not values. Is there any truth to this?
No, according to the php manual mysqli_result::fetch_array returns an array of strings that corresponds to the fetched row or NULL if there are no more rows in resultset.
Null is falsy so the while loop will not be entered.
Although the if statement is unnecessary if you had one you would use mysqli_result::$num_rows to check if the query returned any rows.
if($movieResult->num_rows > 0){
while($m = $movieResult->fetch_array(MYSQLI_ASSOC)){
...
}
}

PHP array presentation

I have an php array with presentation as follow:-
<?php
$ads = array();
$ads [] = array(
'name' => 'Apple',
'duration' => '3',
'price' => "$5"
);
$ads [] = array(
'name' => 'Orange',
'duration' => '2',
'price' => "$10"
);
$ads [] = array(
'name' => 'Banana',
'duration' => '5',
'price' => "$6"
);
and then, I would like to replace the static data with dynamic data from database:-
$sql = "SELECT * from tb_fruit order by fruit_id ASC";
$result = mysql_query($sql_approve, $conn_fruit);
while($record = mysql_fetch_array($result))
{
$fruit_id = $record['fruit_id'];
$fruit_name = $record['fruit_name '];
$fruit_price= $record['fruit_price'];
$fruit_duration= $record_approve['fruit_duration'];
}
Actually, how shall I combine the 2 presentations together? Thanks!
If you want to get a similar structure as the one that you have in the first example, you can modify your second to create a new array and append it to an existing $ads array.
$sql = "SELECT * from tb_fruit order by fruit_id ASC";
$result = mysql_query($sql_approve, $conn_fruit);
$ads = array();
while($record = mysql_fetch_array($result))
{
$ads[] = array(
'name' => $record['fruit_name'],
'price' => $record['fruit_price'],
'duration' => $record['fruit_duration']);
}
iteration over result can be modified (provided only the required attributes are fetched in the query)
$fruit = array();
while($record = mysql_fetch_array($result))
{
$fruit[] = $record;
}
may be you could use array_merge
$result = array();
$result = array_merge($ads,$fruit);
So, you want to populate your "ads" array with information from the database? It seems fairly counterintuitive seeing as mysql_fetch_array already returns a perfectly adequate associative array, but here you go:
$sql = "SELECT * from tb_fruit order by fruit_id ASC";
$result = mysql_query($sql_approve, $conn_fruit);
$ads = array();
while($record = mysql_fetch_array($result))
{
$ads[] = array (
'name' => $record['fruit_name'],
'price' => $record['fruit_price'],
'duration' => $record['fruit_duration']
);
}
If
the initial array is not just an example, but default data that you might overwrite with the db data, and
the fruit name is unique (a primary key of sorts)
then you should set the array key of the main array to that of the fruit name, so that if the db has a different value, it will automatically get overwritten but otherwise left alone. Like so:
$ads ['Apple'] = array(
'duration' => '3',
'price' => "$5"
);
$ads ['Orange'] = array(
'duration' => '2',
'price' => "$10"
);
$ads ['Banana'] = array(
'duration' => '5',
'price' => "$6"
);
$sql = "SELECT * from tb_fruit order by fruit_id ASC";
$result = mysql_query($sql_approve, $conn_fruit);
$ads = array();
while($record = mysql_fetch_array($result))
{
$ads[$record['fruit_name']]['price'] = $record['fruit_price'];
$ads[$record['fruit_name']]['duration'] = $record['fruit_duration'];
}
Now, if 'Apple' is in the db, the price and duration get overwritten, but otherwise, it stays where you set it before the query.
You could go a step further and have both the price and duration returned by the query checked for an empty value (Null, "", 0), and only set the value if it is not empty, etc. But that is more a of business logic decision.

Categories