I would like to know if it is possible to insert multiple rows using one prepared statement.
Below is an example of how I would normally insert one row into the db:
$params=array();
$params[':val1']="val1";
$params[':val2']="val2";
$params[':val3']="val3";
$sql="INSERT INTO table VALUES (col1,col2,col3) VALUES (:val1,:val2,:val3)";
$stmt=DB::getInstance()->prepare($sql);
$stmt->execute($params);
The values I want to insert will come from an array, for example:
$values[0]['val1'];
$values[0]['val2'];
$values[0]['val3'];
$values[1]['val1'];
$values[2]['val2'];
etc.
This code may have to insert a few hundred rows at once, I thought about creating a loop to create hundreds of params and then append the sql statement with an extra insert for each row but I thought there must be a better way. What would be the best way to do this?
The first important thing to say is that you can insert multiple rows thanks to only one INSERT query
INSERT INTO Table (col1, col2, col3)
VALUES ('abc', 'def', 'ghi'),
('abc', 'def', 'ghi'),
('abc', 'def', 'ghi'),
('abc', 'def', 'ghi'),
('abc', 'def', 'ghi')
-- and so on...
Once you know that, you're able to get a good solution with PDO (for instance).
You have to keep in mind that you want a complete prepare and execute process (in term of security, you have to pass each parameter separately).
Let's say you have rows to insert structured as follow:
$rows = array(
array('abc', 'def', 'ghi'), // row 1 to insert
array('abc', 'def', 'ghi'), // row 2 to insert
array('abc', 'def', 'ghi') // row 3 to insert
// and so on ...
);
Your goal is to have this result as a prepared query:
INSERT INTO Table (col1, col2, col3)
VALUES (?, ?, ?),
(?, ?, ?),
(?, ?, ?)
With its corresponding execute:
PDOStatement::execute(array('abc', 'def', 'ghi', 'abc', 'def', 'ghi', 'abc', 'def', 'ghi'));
Well, you only have to do it now:
$rows = array(
array('abc', 'def', 'ghi'),
array('abc', 'def', 'ghi'),
array('abc', 'def', 'ghi')
);
$row_length = count($rows[0]);
$nb_rows = count($rows);
$length = $nb_rows * $row_length;
/* Fill in chunks with '?' and separate them by group of $row_length */
$args = implode(',', array_map(
function($el) { return '('.implode(',', $el).')'; },
array_chunk(array_fill(0, $length, '?'), $row_length)
));
$params = array();
foreach($rows as $row)
{
foreach($row as $value)
{
$params[] = $value;
}
}
$query = "INSERT INTO Table (col1, col2, col3) VALUES ".$args;
$stmt = DB::getInstance()->prepare($query);
$stmt->execute($params);
And... That's it!
This way, each param is treated separately, which is what you want (security, security, security!) and all of it, in a dynamic way, with only one INSERT query
If you have too many rows to insert (see this), you should execute one by one
$rows = array(
array('abc', 'def', 'ghi'), // row 1 to insert
array('abc', 'def', 'ghi'), // row 2 to insert
array('abc', 'def', 'ghi') // row 3 to insert
// and so on ...
);
$args = array_fill(0, count($rows[0]), '?');
$query = "INSERT INTO Table (col1, col2, col3) VALUES (".implode(',', $args).")";
$stmt = $pdo->prepare($query);
foreach ($rows as $row)
{
$stmt->execute($row);
}
If your table is transactional (for example an InnoDB), you can use a Transaction to speed up your insertions. A transaction has also the advantage of roll backs.
$pdo = DB::getInstance();
$stmt = $pdo->prepare('INSERT INTO table VALUES (col1, col2, col3) VALUES (:val1, :val2, :val3)');
$pdo->beginTransaction();
// The queries are not executed yet, but pushed to a transaction "stack"
foreach ($values as $value) {
$stmt->execute([
':val1' => $value['val1'],
':val2' => $value['val2'],
':val3' => $value['val3'],
]);
}
// Executes all the queries "at once"
$pdo->commit();
If you're only inserting a few hundred rows, I'd favor simpler code like the following. Prepare a single-row INSERT statement, and then loop over your data array, executing the prepared query once for each row.
$rows = array(
array('abc', 'def', 'ghi'), // row 1 to insert
array('abc', 'def', 'ghi'), // row 2 to insert
array('abc', 'def', 'ghi') // row 3 to insert
// and so on ...
);
$params = implode(",", array_fill(0, count($rows[0]), "?"));
$sql = "INSERT INTO mytable VALUES ($params)";
$stmt = $pdo->prepare($sql); // rely on exceptions for error detection
foreach ($rows as $row) {
$stmt->execute($row);
}
MySQL does support multi-row INSERT syntax of course, so you could try putting that together.
$params = implode(",", array_fill(0, count($rows[0]), "?"));
$tuples = "(" . implode("),(", array_fill(0, count($rows), $params)) . ")";
$sql = "INSERT INTO mytable VALUES $tuples";
$values = call_user_func_array("array_merge", $rows);
$stmt = $pdo->prepare($sql);
$stmt->execute($values);
But if you try to create a single INSERT statement with as many tuples as the items in your data array, you might accidentally generate an SQL statement that is longer than the maximum packet length.
If you have thousands of rows, enough so that executing a prepared statement one row at a time is too much overhead, you should use LOAD DATA INFILE.
To keep your code, you have to make a loop for executing all insertions you need :
$array_params = array();
$params[':val1']="val1 1";
$params[':val2']="val1 2";
$params[':val3']="val1 3";
$array_params[] = $params;
$params[':val1']="val2 1";
$params[':val2']="val2 2";
$params[':val3']="val2 3";
$array_params[] = $params;
$sql="INSERT INTO table (col1,col2,col3) VALUES (:val1,:val2,:val3)";
$stmt=DB::getInstance()->prepare($sql);
foreach($array_params as $params)
{
$stmt->execute($params);
}
But its possible to execute multiple insertions with one query like INSERT INTO table (col1,col2,col3) VALUES ("val1","val2","val3"),("val4","val5","val6"),("val7","val8,"val9"), by using something like this to build the query:
$all_inserts = array( array('val1', 'val2', 'val3'),array('val4', 'val5', 'val6'));
$sql = 'INSERT INTO table (col1,col2,col3) VALUES ';
$rows = array();
foreach ($all_inserts as $one_insert)
{
$rows[] = '('.implode(',', $pdo->quote($one_insert).')';
}
$sql .= ' '.implode(',', $rows);
$pdo->query($sql);
Related
I have inserted some strings with values in one table lets call it table_1 in my database, now I have arrays which I want to insert into separate rows in table_2 in my SQL database.
$sql = 'INSERT INTO ' . $table_1 . '(shipping_fee, waybill_status, pickup_fee, )
VALUES(:shipping_fee, :waybill_status, :pickup_fee)';
$stmt = $this->dbConn->prepare($sql);
$stmt->bindParam(':shipping_fee', $s_shipping_fee);
$stmt->bindParam(':waybill_status', $s_waybill_status);
$stmt->bindParam(':pickup_fee', $this->pickup_fee);
if($stmt->execute()){ //THIS INSERTED THE STRINGS PERFECTLY
//NOW ALL VALUES TO BE INSERT INTO $sqal is an array
$sqal = 'INSERT INTO ' . $table_2. '(id, waybill_number, client_id, item_name, item_weight, item_length, item_width, item_category, date_added) VALUES(null, :waybill_numberr, :client_idaa, :item_name, :item_weight, :item_length, :item_width, :item_category, :date_added)';
$stmtaaa = $this->dbConn->prepare($sqal);
$stmtaaa->bindParam(':item_name', $this->item_name); //ARRAY
$stmtaaa->bindParam(':item_weight', $this->item_weight); //ARRAY
$stmtaaa->bindParam(':item_length', $this->item_length); //ARRAY
$stmtaaa->bindParam(':item_width', $this->item_width); //ARRAY
$stmtaaa->bindParam(':item_category', $this->item_category); //ARRAY
$stmtaaa->execute(); //HoW do I go about this.
} else {
echo "Could not insert";
exit();
}
You had a syntax error in your first query, the trailing commas , should not be there in the column- or value-list.
You can insert an array by executing the prepare multiple times with different values. This example assumes that all your arrays are indexed by numbers (from zero and up).
The code example above also binds more columns than it binds, so you need to bind a value to each column. waybill_numberr, client_idaa and date_added are missing its binds (I just added some random placeholders).
$sql = "INSERT INTO $table_1 (shipping_fee, waybill_status, pickup_fee)
VALUES (:shipping_fee, :waybill_status, :pickup_fee)";
$stmt = $this->dbConn->prepare($sql);
$stmt->bindParam(':shipping_fee', $s_shipping_fee);
$stmt->bindParam(':waybill_status', $s_waybill_status);
$stmt->bindParam(':pickup_fee', $this->pickup_fee);
if ($stmt->execute()) {
$sqal = "INSERT INTO $table_2 (id, waybill_number, client_id, item_name, item_weight, item_length, item_width, item_category, date_added)
VALUES (null, :waybill_numberr, :client_idaa, :item_name, :item_weight, :item_length, :item_width, :item_category, :date_added)";
$stmtaaa = $this->dbConn->prepare($sqal);
foreach ($this->item_weight as $key => $value) {
$stmtaaa->execute(["waybill_numberr" => '1', // Change this to your actual value
"client_idaa" => '1', // Change this to your actual value
"item_name" => $value,
"item_weight" => $this->item_weight[$key],
"item_length" => $this->item_length[$key],
"item_width" => $this->item_width[$key],
"item_category" => $this->item_category[$key],
"date_added" => '1']);
}
} else {
echo "Could not insert";
exit();
}
Here is the piece of code so far I've tried:
$month = array('red','green','red');
$values = array();
foreach($month as $dataset)
{
$values[] = ($dataset);
}
$columns = implode(", ",array_keys($values));
$escaped_values = array_values($values);
$valu = implode(", ", $escaped_values);
$sql = "INSERT INTO abc (col1,col2,col3) VALUES ('$valu');";
Here is the output:
Error: INSERT INTO abc (col1,col2,col3) VALUES ('red, green, red');
Column count doesn't match value count at row 1
What I am trying to do is to store values in the array where the value of the array may vary depending upon the value the user gave, and then store it in different columns. For example, if the total columns are 3 and array value is 2 then store values in col1 and col2 and null value in col3.
With the single quotes around the whole string 'red, green, red' that is the value for col1 only.
It should look more like this 'red','green','red'.
So quick fix is this $valu = implode("','", $escaped_values);
Added a single quotes inside your implode.
The outside quotes will be captured in the final statement as detailied in the problem above:
$sql = "INSERT INTO abc (col1,col2,col3) VALUES ('$valu');";
Here the Code after making it work properly
$month = array('red','green','red');
$values = array();
foreach($month as $dataset)
{
$values[] = "'{$dataset}'";
}
$columns = implode(", ",array_keys($values));
$escaped_values = array_values($values);
$valu = implode(", ", $escaped_values);
$sql = "INSERT INTO abc (col1,col2,col3) VALUES ($valu);";
The values are into the single quote. Please check below example.
INSERT INTO abc (col1,col2,col3) VALUES ('red', 'green', 'red');
No one else seems to have addressed the actual issue.
What I am trying to do is to store values in the array where the value
of the array may vary depending upon the value the user gave, and then
store it in different columns. For example, if the total columns are 3
and array value is 2 then store values in col1 and col2 and null value
in col3.
So if the values come in as an array at different lengths, and you want to insert as null or limit to the max length of the columns then you can do it like the following.
Ideally, you want to produce an array which looks like:
$data = [
'col1' => 'red',
'col2' => 'green',
'col3' => null
];
To do that without any looping define the database columns, then create an array of the same length of null values, then slice the input array to the same length and merge, this will produce an array like above.
<?php
$columns = [
'col1',
'col2',
'col3',
];
$month = [
'red',
'green'
];
$data = array_combine(
$columns,
array_slice($month, 0, count($columns))+array_fill(0, count($columns), null)
);
Now you simply need to implode the array into your query, using ? for placeholders for the prepared query.
$sql = '
INSERT INTO abc (
'.implode(', ', array_keys($data)).'
) VALUES (
'.implode(', ', array_fill(0, count($data), '?')).'
)';
Will produce:
INSERT INTO abc (
col1, col2, col3
) VALUES (
?, ?, ?
)
Then just do your query, for example:
$stmt = $pdo->prepare($sql);
$stmt->execute($data);
Simple clean and safe.
insert into test (sometext) values ("?"),("?")
$a= array("weird' text","sdfa");
I want to insert text into the table test in column sometext using bind parameter and I do not want the execute statement in a loop. I cannot implode the array in ("?"),("?") form as the query might crash coz the text can be composed of quotes.
So is there a way to achieve this using PDO in one(1) execute statement?
I cannot implode the array in ("?"),("?") form as the query might crash coz the text can be composed of quotes.
The prepared statements are there to solve quoting/escaping problems.
This syntax is wrong1:
insert into test (sometext) values ("?"),("?")
You don't have to wrap parameters by quotes, you have to write query in this form:
INSERT INTO test (sometext) VALUES (?),(?)
Then, you can use implode() without worrying about quotes:
$a = array( "weird' text", "sdfa" );
$query = "INSERT INTO test (sometext) VALUES (" . implode( "),(", array_fill( 0, count( $a ), "?" ) ) . ")";
$stmt = $db->prepare( $query );
$stmt->execute( $a );
As alternative, you can use substr and str_repeat instead of implode:
$query = "INSERT INTO test (sometext) VALUES " . substr( str_repeat( "(?),", count( $a ) ), 0, -1 );
1 Using insert into test (sometext) values ("?"),("?") you insert in your fields literally two question marks.
$stmt = $conn->prepare("INSERT INTO test (field1, field2, field3) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $field1, $field2, $field3);
// set parameters and execute
$field1 = "test";
$field2 = "test2";
$field3 = "test#test.cc";
$stmt->execute();
i know there is a bulk insert in PHP that i can resolve easily in php
$list = array();
foreach($list as $key->$value){ $list[] = "($key, $value)"; }
$query = "insert into tblTest (a, b, c) values ".implode(",", $list);
$r = mysql_query($q);
and i know how to do a insert or update statement:
$insert into tblTest (a,b,c) values (1,2,3) on duplicate key update b=5;
I was not sure if this was the sufficient answer query i would want to run:
INSERT INTO tblTest (a, b, c)
VALUES(1, 2, 3) ON DUPLICATE KEY UPDATE b=VALUES(b), c=VALUES(c)
I have to insert single set of data multiple times , say n rows.
INSERT INTO MyTable VALUES ("John", 123, "US");
Can I insert all n rows in a single SQL statement?
here n is dynamic value n is user input , how to make insert query n times , any idea.
$sql = "INSERT INTO `mytable` VALUES(`field1`,`field2`,`field3`) VALUES ";
$count = 5;
for($i=0;$i<$coutn;$i++)
{
$sql .= " ('john','123','us' )";
}
is this correct way..
Yep, this can be done easily, it should look something like this:
INSERT INTO MyTable VALUES ("John", 123, "US"), ("Carl", 123, "EU"), ("Jim", 123, "FR");
However, it is good programming practice to specify the columns of your table in the query, for example:
INSERT INTO MyTable (Column1, Column2, Column3)
VALUES ("John", 123, "US"), ("Carl", 123, "EU"), ("Jim", 123, "FR");
EDIT : You can build your query like this (in for cycle), the $total is your user input:
$sql = "INSERT INTO MyTable (Column1, Column2, Column3) VALUES";
//Build SQL INSERT query
for ($i = 1; $i <= $total; $i++) {
$sql .= " ($value1, $value2, $value3), ";
}
//Trim the last comma (,)
$sql = rtrim($sql,",");
//Now, the $sql var contains the complex query.
$result = mysql_query($sql);
As you can see we do not execute the INSERT statement in the loop, but rather we build the SQL query text and then we will execute it in one pass.