Related
Say, we have multiple rows to be inserted in a table:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ] //[ array of values ];
Using PDO:
$sql = "insert into `table_name` (col1, col2, col3) values (?, ?, ?)" ;
Now, how should you proceed in inserting the rows? Like this?
$stmt = $db->prepare($sql);
foreach($rows as $row){
$stmt->execute($row);
}
or, like this?
$sql = "insert into `table_name` (col1, col2, col3) values ";
$sql .= //not sure the best way to concatenate all the values, use implode?
$db->prepare($sql)->execute();
Which way would be faster and safer? What is the best way to insert multiple rows?
You have at least these two options:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ];
$sql = "insert into `table_name` (col1, col2, col3) values (?,?,?)";
$stmt = $db->prepare($sql);
foreach($rows as $row)
{
$stmt->execute($row);
}
OR:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ];
$sql = "insert into `table_name` (col1, col2, col3) values ";
$paramArray = array();
$sqlArray = array();
foreach($rows as $row)
{
$sqlArray[] = '(' . implode(',', array_fill(0, count($row), '?')) . ')';
foreach($row as $element)
{
$paramArray[] = $element;
}
}
// $sqlArray will look like: ["(?,?,?)", "(?,?,?)", ... ]
// Your $paramArray will basically be a flattened version of $rows.
$sql .= implode(',', $sqlArray);
$stmt = $db->prepare($sql);
$stmt->execute($paramArray);
As you can see the first version features a lot simpler code; however the second version does execute a batch insert. The batch insert should be faster, but I agree with #BillKarwin that the performance difference will not be noticed in the vast majority of implementations.
I would do it the first way, prepare the statement with one row of parameter placeholders, and insert one row at a time with execute.
$stmt = $db->prepare($sql);
foreach($rows as $row){
$stmt-> execute($row);
}
It's not quite as fast as doing multiple rows in a single insert, but it's close enough that you will probably never notice the difference.
And this has the advantage that it's very easy to work with the code. That's why you're using PHP anyway, for the developer efficiency, not the runtime efficiency.
If you have many rows (hundreds or thousands), and performance is a priority, you should consider using LOAD DATA INFILE.
You can also go this way:
<?php
$qmarks = '(?,?,?)'. str_repeat(',(?,?,?)', count($rows)-1);
$sql = "INSERT INTO `table`(col1,col2,col3) VALUES $qmarks";
$vals = array();
foreach($rows as $row)
$vals = array_merge($vals, $row);
$db->prepare($sql)->execute($vals);
To be honest, I don't know which one will be faster, all depends on the delay between mysql and the php server.
/* test.php */
<?php
require_once('Database.php');
$obj = new Database();
$table = "test";
$rows = array(
array(
'name' => 'balasubramani',
'status' => 1
),
array(
'name' => 'balakumar',
'status' => 1
),
array(
'name' => 'mani',
'status' => 1
)
);
var_dump($obj->insertMultiple($table,$rows));
?>
/* Database.php */
<?php
class Database
{
/* Initializing Database Information */
var $host = 'localhost';
var $user = 'root';
var $pass = '';
var $database = "database";
var $dbh;
/* Connecting Datbase */
public function __construct(){
try {
$this->dbh = new PDO('mysql:host='.$this->host.';dbname='.$this->database.'', $this->user, $this->pass);
//print "Connected Successfully";
}
catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
/* Insert Multiple Rows in a table */
public function insertMultiple($table,$rows){
$this->dbh->beginTransaction(); // also helps speed up your inserts.
$insert_values = array();
foreach($rows as $d){
$question_marks[] = '(' . $this->placeholders('?', sizeof($d)) . ')';
$insert_values = array_merge($insert_values, array_values($d));
$datafields = array_keys($d);
}
$sql = "INSERT INTO $table (" . implode(",", $datafields ) . ") VALUES " . implode(',', $question_marks);
$stmt = $this->dbh->prepare ($sql);
try {
$stmt->execute($insert_values);
} catch (PDOException $e){
echo $e->getMessage();
}
return $this->dbh->commit();
}
/* placeholders for prepared statements like (?,?,?) */
function placeholders($text, $count=0, $separator=","){
$result = array();
if($count > 0){
for($x=0; $x<$count; $x++){
$result[] = $text;
}
}
return implode($separator, $result);
}
}
?>
The above code should be good solution for Inserting Multiple Records using PDO.
I am trying to UPDATE every field (16 fields plus the key) in a record in a MySQL database with some new data from a form using php. It works fine with INSERT but when I try to change to UPDATE it won't do it. I also feel this is a very long way to do it and there is probably a more iterative solution, I would really appreciate some help please:
<?php
require_once 'login.php';
$con=mysqli_connect($hh,$un,$pw,$db);
// Check connection
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
echo 'Connected successfully';
$sql = "UPDATE PiBQ_Config
SET (upButton, dnButton, stepCore1, stepCore2, stepCore3, stepCore4, limSwitch, waitTime, maxPosn, pidKp, pidKi, pidKd, intMax, intMin, sleepTime, progRun) =
('$_POST[upButton]', '$_POST[dnButton]', '$_POST[stepCore1]', '$_POST[stepCore2]', '$_POST[stepCore3]', '$_POST[stepCore4]', '$_POST[limSwitch]', '$_POST[waitTime]', '$_POST[maxPosn]', '$_POST[pidKp]', '$_POST[pidKi]', '$_POST[pidKd]', '$_POST[intMax]', '$_POST[intMin]', '$_POST[sleepTime]', '$_POST[progRun]')
WHERE tableKey = 1";
mysqli_query($con,$sql);
echo "1 record added";
header ('location: ../settings.php');
mysql_close($con)
?>
Set each field individually for example:
"UPDATE PiBQ_Config
SET upButton = '$_POST[upButton]',
dnButton ='$_POST[upButton]',
stepCore1 = '$_POST[dnButton]',
.
.
.//the rest of variables
WHERE tableKey = 1"
This method creates a query from three parameters: the table name, an associative array which has the name of the columns as keys and another whitch has the name of the column you want to update as keys for the where statement.
public function update($table, $data, $where)
{
$query='UPDATE `'.$table.'` SET ';
foreach($data as $key => $value)
{
$query .= '`'.$key.'`=:'.$key.',';
}
$query = substr($query, 0, -1);
$query .= ' WHERE ';
foreach($where as $key => $value)
{
$query .= '`'.$key.'`=:'.$key.',';
}
$query = substr($query, 0, -1);
$data += $where;
$update = $this->db->prepare($query);
$update->execute($data);
}
It will build a query and execute it. You should use PDO and prepared statements for more security.
Example:
for example you want to update the name of a user in the database.
$firstName = 'newFirstName';
$lastName = 'newLastName';
$id = idOfUserYouWantToUpdate;
$table = 'users';
$data = array('user_firstname'=>$firstName, 'user_lastname'=>$lastName);
$where = array('user_id'=>$id)
update($table, $data, $where);
Say, we have multiple rows to be inserted in a table:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ] //[ array of values ];
Using PDO:
$sql = "insert into `table_name` (col1, col2, col3) values (?, ?, ?)" ;
Now, how should you proceed in inserting the rows? Like this?
$stmt = $db->prepare($sql);
foreach($rows as $row){
$stmt->execute($row);
}
or, like this?
$sql = "insert into `table_name` (col1, col2, col3) values ";
$sql .= //not sure the best way to concatenate all the values, use implode?
$db->prepare($sql)->execute();
Which way would be faster and safer? What is the best way to insert multiple rows?
You have at least these two options:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ];
$sql = "insert into `table_name` (col1, col2, col3) values (?,?,?)";
$stmt = $db->prepare($sql);
foreach($rows as $row)
{
$stmt->execute($row);
}
OR:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ];
$sql = "insert into `table_name` (col1, col2, col3) values ";
$paramArray = array();
$sqlArray = array();
foreach($rows as $row)
{
$sqlArray[] = '(' . implode(',', array_fill(0, count($row), '?')) . ')';
foreach($row as $element)
{
$paramArray[] = $element;
}
}
// $sqlArray will look like: ["(?,?,?)", "(?,?,?)", ... ]
// Your $paramArray will basically be a flattened version of $rows.
$sql .= implode(',', $sqlArray);
$stmt = $db->prepare($sql);
$stmt->execute($paramArray);
As you can see the first version features a lot simpler code; however the second version does execute a batch insert. The batch insert should be faster, but I agree with #BillKarwin that the performance difference will not be noticed in the vast majority of implementations.
I would do it the first way, prepare the statement with one row of parameter placeholders, and insert one row at a time with execute.
$stmt = $db->prepare($sql);
foreach($rows as $row){
$stmt-> execute($row);
}
It's not quite as fast as doing multiple rows in a single insert, but it's close enough that you will probably never notice the difference.
And this has the advantage that it's very easy to work with the code. That's why you're using PHP anyway, for the developer efficiency, not the runtime efficiency.
If you have many rows (hundreds or thousands), and performance is a priority, you should consider using LOAD DATA INFILE.
You can also go this way:
<?php
$qmarks = '(?,?,?)'. str_repeat(',(?,?,?)', count($rows)-1);
$sql = "INSERT INTO `table`(col1,col2,col3) VALUES $qmarks";
$vals = array();
foreach($rows as $row)
$vals = array_merge($vals, $row);
$db->prepare($sql)->execute($vals);
To be honest, I don't know which one will be faster, all depends on the delay between mysql and the php server.
/* test.php */
<?php
require_once('Database.php');
$obj = new Database();
$table = "test";
$rows = array(
array(
'name' => 'balasubramani',
'status' => 1
),
array(
'name' => 'balakumar',
'status' => 1
),
array(
'name' => 'mani',
'status' => 1
)
);
var_dump($obj->insertMultiple($table,$rows));
?>
/* Database.php */
<?php
class Database
{
/* Initializing Database Information */
var $host = 'localhost';
var $user = 'root';
var $pass = '';
var $database = "database";
var $dbh;
/* Connecting Datbase */
public function __construct(){
try {
$this->dbh = new PDO('mysql:host='.$this->host.';dbname='.$this->database.'', $this->user, $this->pass);
//print "Connected Successfully";
}
catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
/* Insert Multiple Rows in a table */
public function insertMultiple($table,$rows){
$this->dbh->beginTransaction(); // also helps speed up your inserts.
$insert_values = array();
foreach($rows as $d){
$question_marks[] = '(' . $this->placeholders('?', sizeof($d)) . ')';
$insert_values = array_merge($insert_values, array_values($d));
$datafields = array_keys($d);
}
$sql = "INSERT INTO $table (" . implode(",", $datafields ) . ") VALUES " . implode(',', $question_marks);
$stmt = $this->dbh->prepare ($sql);
try {
$stmt->execute($insert_values);
} catch (PDOException $e){
echo $e->getMessage();
}
return $this->dbh->commit();
}
/* placeholders for prepared statements like (?,?,?) */
function placeholders($text, $count=0, $separator=","){
$result = array();
if($count > 0){
for($x=0; $x<$count; $x++){
$result[] = $text;
}
}
return implode($separator, $result);
}
}
?>
The above code should be good solution for Inserting Multiple Records using PDO.
Say, we have multiple rows to be inserted in a table:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ] //[ array of values ];
Using PDO:
$sql = "insert into `table_name` (col1, col2, col3) values (?, ?, ?)" ;
Now, how should you proceed in inserting the rows? Like this?
$stmt = $db->prepare($sql);
foreach($rows as $row){
$stmt->execute($row);
}
or, like this?
$sql = "insert into `table_name` (col1, col2, col3) values ";
$sql .= //not sure the best way to concatenate all the values, use implode?
$db->prepare($sql)->execute();
Which way would be faster and safer? What is the best way to insert multiple rows?
You have at least these two options:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ];
$sql = "insert into `table_name` (col1, col2, col3) values (?,?,?)";
$stmt = $db->prepare($sql);
foreach($rows as $row)
{
$stmt->execute($row);
}
OR:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ];
$sql = "insert into `table_name` (col1, col2, col3) values ";
$paramArray = array();
$sqlArray = array();
foreach($rows as $row)
{
$sqlArray[] = '(' . implode(',', array_fill(0, count($row), '?')) . ')';
foreach($row as $element)
{
$paramArray[] = $element;
}
}
// $sqlArray will look like: ["(?,?,?)", "(?,?,?)", ... ]
// Your $paramArray will basically be a flattened version of $rows.
$sql .= implode(',', $sqlArray);
$stmt = $db->prepare($sql);
$stmt->execute($paramArray);
As you can see the first version features a lot simpler code; however the second version does execute a batch insert. The batch insert should be faster, but I agree with #BillKarwin that the performance difference will not be noticed in the vast majority of implementations.
I would do it the first way, prepare the statement with one row of parameter placeholders, and insert one row at a time with execute.
$stmt = $db->prepare($sql);
foreach($rows as $row){
$stmt-> execute($row);
}
It's not quite as fast as doing multiple rows in a single insert, but it's close enough that you will probably never notice the difference.
And this has the advantage that it's very easy to work with the code. That's why you're using PHP anyway, for the developer efficiency, not the runtime efficiency.
If you have many rows (hundreds or thousands), and performance is a priority, you should consider using LOAD DATA INFILE.
You can also go this way:
<?php
$qmarks = '(?,?,?)'. str_repeat(',(?,?,?)', count($rows)-1);
$sql = "INSERT INTO `table`(col1,col2,col3) VALUES $qmarks";
$vals = array();
foreach($rows as $row)
$vals = array_merge($vals, $row);
$db->prepare($sql)->execute($vals);
To be honest, I don't know which one will be faster, all depends on the delay between mysql and the php server.
/* test.php */
<?php
require_once('Database.php');
$obj = new Database();
$table = "test";
$rows = array(
array(
'name' => 'balasubramani',
'status' => 1
),
array(
'name' => 'balakumar',
'status' => 1
),
array(
'name' => 'mani',
'status' => 1
)
);
var_dump($obj->insertMultiple($table,$rows));
?>
/* Database.php */
<?php
class Database
{
/* Initializing Database Information */
var $host = 'localhost';
var $user = 'root';
var $pass = '';
var $database = "database";
var $dbh;
/* Connecting Datbase */
public function __construct(){
try {
$this->dbh = new PDO('mysql:host='.$this->host.';dbname='.$this->database.'', $this->user, $this->pass);
//print "Connected Successfully";
}
catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
/* Insert Multiple Rows in a table */
public function insertMultiple($table,$rows){
$this->dbh->beginTransaction(); // also helps speed up your inserts.
$insert_values = array();
foreach($rows as $d){
$question_marks[] = '(' . $this->placeholders('?', sizeof($d)) . ')';
$insert_values = array_merge($insert_values, array_values($d));
$datafields = array_keys($d);
}
$sql = "INSERT INTO $table (" . implode(",", $datafields ) . ") VALUES " . implode(',', $question_marks);
$stmt = $this->dbh->prepare ($sql);
try {
$stmt->execute($insert_values);
} catch (PDOException $e){
echo $e->getMessage();
}
return $this->dbh->commit();
}
/* placeholders for prepared statements like (?,?,?) */
function placeholders($text, $count=0, $separator=","){
$result = array();
if($count > 0){
for($x=0; $x<$count; $x++){
$result[] = $text;
}
}
return implode($separator, $result);
}
}
?>
The above code should be good solution for Inserting Multiple Records using PDO.
I have two forms that share some same columns: NAME, EMAIL, NOMINEE, DEPT, RANK and TIMESTAMP
and I want to store those same columns in a new table called: TEACHING_AWARD_ALL_NOMINATIONS.
I have came up with the code to store each data into different table and then there should be some code to store the shared columns into TEACHING_AWARD_ALL_NOMINATIONS table.
I haven't came up with the code yet, please help!!!!
$srr = array_map('mysql_escape_string', $_REQUEST);
if ($srr['NOMINATIONTYPE'] == 'STUDENT')
$fields = Array('NAME', 'EMAIL', 'NOMINEE', 'DEPT', 'COURSE', 'YEARTERM', 'REQUIRED_FOR_MAJOR', 'MAJOR_LEARNING_OBJECTIVES', 'WHAT_EXTENT_INSTRUCTOR_HELP', 'RANK', 'RANK_COMMENT', 'TEXTBOX_1', 'TEXTBOX_2', 'TEXTBOX_3', 'TEXTBOX_4', 'TEXTBOX_5');
else if ($srr['NOMINATIONTYPE'] == 'FACULTY')
$fields = Array('NAME', 'EMAIL', 'NOMINEE', 'DEPT', 'RANK', 'TEXTBOX_1', 'TEXTBOX_2', 'TEXTBOX_3');
else die('error: no nomination type');
foreach ($fields as $f)
$$f = $srr[$f];
$qry = "INSERT INTO TEACHING_AWARD_".$srr['NOMINATIONTYPE']."_NOMINATIONS (";
foreach ($fields as $f) $qry .= $f . ", ";
$qry = substr($qry, 0, -2);
$qry .= ") VALUES (";
foreach ($fields as $f) $qry .= "'" . $$f . "', ";
$qry = substr($qry, 0, -2);
$qry .= ")";
$result = mysql_query($qry) or die('An error ocurred: '.mysql_error());
echo 'Success! Thank you for submitting your nomination.';
WARNING! As noted in comments your original code is using deprecated functions and suffers from potential security issues.
Check out the refactored solution that is using PDO, and yes you
should use PDO too (instead of your current approach).
Check out the code below, untested but should do your job. As written above it uses PDO - check the docs here
// obviously, first set your connection parameters $DbHost, $DbName etc.
//connect to mysql
$dbh = new PDO("mysql:host=".$DbHost.";dbname=".$DbName, $DbUser, $DbPass, array(PDO::ATTR_PERSISTENT => true));
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbh->query('SET NAMES UTF8'); //assuming you use utf-8 encoding
//provide values array
$values = $YourValuesArray;
//set field types
$fields = array();
$fieldsStudent = Array('NAME', 'EMAIL', 'NOMINEE', 'DEPT', 'COURSE', 'YEARTERM', 'REQUIRED_FOR_MAJOR', 'MAJOR_LEARNING_OBJECTIVES', 'WHAT_EXTENT_INSTRUCTOR_HELP', 'RANK', 'RANK_COMMENT', 'TEXTBOX_1', 'TEXTBOX_2', 'TEXTBOX_3', 'TEXTBOX_4', 'TEXTBOX_5');
$fieldsFaculty = Array('NAME', 'EMAIL', 'NOMINEE', 'DEPT', 'RANK', 'TEXTBOX_1', 'TEXTBOX_2', 'TEXTBOX_3');
$fieldsAll = array_intersect($fieldsStudent, $fieldsFaculty);
//pick the proper field set or die
$fields = ($srr['NOMINATIONTYPE'] == 'STUDENT') ? $fieldsStudent : $fieldsFaculty;
if(empty($fields)) die('error: no nomination type');
//set tables
$table = 'TEACHING_AWARD_'.$srr['NOMINATIONTYPE'].'_NOMINATIONS';
$tableAll = 'TEACHING_AWARD_ALL_NOMINATIONS';
//construct fields string
$strFields = implode(",", $fields);
$strFieldsAll = implode(",", $fieldsAll);
//construct the placeholders string
$strIns = implode(",", array_map(function($item){ return ":".$item; }, $fields));
$strInsAll = implode(",", array_map(function($item){ return ":".$item; }, $fieldsAll));
//insert specific data
$sql = "INSERT INTO $table ($strFields) VALUES ($strIns)";
$sth = $dbh->prepare($sql);
//bind values to placeholders
foreach ($fields as $f)
{
$sth->bindValue(':' . $f, $values[$f]);
}
$sth->execute();
//insert all data
$sql = "INSERT INTO $tableAll ($strFieldsAll) VALUES ($strInsAll)";
$sth = $dbh->prepare($sql);
//bind values to placeholders
foreach ($fieldsAll as $f)
{
$sth->bindValue(':' . $f, $values[$f]);
}
$sth->execute();
EDIT:
As per your comment, to select a UNIONed set of results using PDO you can use the below (untested) code:
//connect to mysql
$dbh = new PDO("mysql:host=".$DbHost.";dbname=".$DbName, $DbUser, $DbPass, array(PDO::ATTR_PERSISTENT => true));
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbh->query('SET NAMES UTF8'); //assuming you use utf-8 encoding
$sql = "SELECT NAME, EMAIL, NOMINEE, DEPT, RANK, TIMESTAMP, 'STUDENT' AS TYPE FROM TEACHING_AWARD_STUDENT_NOMINATIONS
UNION
SELECT NAME, EMAIL, NOMINEE, DEPT, RANK, TIMESTAMP, 'FACULTY' AS TYPE FROM TEACHING_AWARD_FACULTY_NOMINATIONS";
$sth = $dbh->prepare($sql);
$sth->execute();
//init result array
$results = array();
//fetch the results into an array
while($row = $sth->fetch(PDO::FETCH_ASSOC))
$results[] = $row;
//show results or do whatever else you need
print_r($results);
You could try making two arrays, one for the "shared" columns (NAME, EMAIL, etc.) and another for the "type-specific" columns (COURSE, YEARTERM, etc.). Then, when you are building up your SQL statement, do a foreach on the "shared" columns followed immediately by the "type-specific" columns to simulate the entire table. It might look something like this:
$qry = "INSERT INTO TEACHING_AWARD_".$srr['NOMINATIONTYPE']."_NOMINATIONS (";
foreach ($fields_shared as $f) $qry .= $f . ", ";
foreach ($fields_typespecific as $f) $qry .= $f . ", ";
$qry = substr($qry, 0, -2);
$qry .= ") VALUES (";
foreach ($fields_shared as $f) $qry .= "'" . $$f . "', ";
foreach ($fields_typespecific as $f) $qry .= "'" . $$f . "', ";
$qry = substr($qry, 0, -2);
$qry .= ")";
I would also agree with th3falc0n's comment about looking into preparing your SQL statements with more robust tools.
Another consideration: rather than enter redundant data into a TEACHING_AWARD_ALL_NOMINATIONS table, why not simply make that a view that pulls all shared columns from each type-specific table (likely using a UNION statement)? This prevents redundant data and would make data maintenance easier (e.g. when a nomination is deleted from one of the type-specific table, it automatically vanishes from the ALL_NOMINATIONS view, etc.).